﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Xml;
using System.Xml.Linq;
using Tridion.ContentManager.CoreService.Client;

namespace TridionDesktopTools.Core
{
    public static class Functions
    {
        #region Fields

        public static ILocalClient Client;
        public static StreamDownloadClient StreamDownloadClient;
        public static StreamUploadClient StreamUploadClient;
        public static BindingType ClientBindingType = BindingType.HttpBinding;

        #endregion

        #region Tridion CoreService

        private static NetTcpBinding GetBinding()
        {
            var binding = new NetTcpBinding
            {
                MaxReceivedMessageSize = 2147483647,
                ReaderQuotas = new XmlDictionaryReaderQuotas
                {
                    MaxStringContentLength = 2097152,
                    MaxArrayLength = 81920,
                    MaxBytesPerRead = 5120,
                    MaxDepth = 32,
                    MaxNameTableCharCount = 81920
                },
                CloseTimeout = TimeSpan.FromMinutes(10),
                OpenTimeout = TimeSpan.FromMinutes(10),
                ReceiveTimeout = TimeSpan.FromMinutes(10),
                SendTimeout = TimeSpan.FromMinutes(10),
                TransactionFlow = true,
                TransactionProtocol = TransactionProtocol.WSAtomicTransaction11
            };
            return binding;
        }

        private static BasicHttpBinding GetHttpBinding()
        {
            var binding = new BasicHttpBinding
            {
                MaxReceivedMessageSize = 2147483647,
                ReaderQuotas = new XmlDictionaryReaderQuotas
                {
                    MaxStringContentLength = 2097152,
                    MaxArrayLength = 81920,
                    MaxBytesPerRead = 5120,
                    MaxDepth = 32,
                    MaxNameTableCharCount = 81920
                },
                CloseTimeout = TimeSpan.FromMinutes(10),
                OpenTimeout = TimeSpan.FromMinutes(10),
                ReceiveTimeout = TimeSpan.FromMinutes(10),
                SendTimeout = TimeSpan.FromMinutes(10),
                MessageEncoding = WSMessageEncoding.Mtom,
                TransferMode = TransferMode.Streamed
            };
            return binding;
        }

        private static BasicHttpBinding GetHttpBinding2()
        {
            var binding = new BasicHttpBinding
            {
                MaxReceivedMessageSize = 2147483647,
                ReaderQuotas = new XmlDictionaryReaderQuotas
                {
                    MaxStringContentLength = 2097152,
                    MaxArrayLength = 81920,
                    MaxBytesPerRead = 5120,
                    MaxDepth = 32,
                    MaxNameTableCharCount = 81920
                },
                CloseTimeout = TimeSpan.FromMinutes(10),
                OpenTimeout = TimeSpan.FromMinutes(10),
                ReceiveTimeout = TimeSpan.FromMinutes(10),
                SendTimeout = TimeSpan.FromMinutes(10),
                Security = new BasicHttpSecurity
                {
                    Mode = BasicHttpSecurityMode.TransportCredentialOnly,
                    Transport = new HttpTransportSecurity
                    {
                        ClientCredentialType = HttpClientCredentialType.Windows
                    },
                },
                MessageEncoding = WSMessageEncoding.Mtom
            };
            return binding;
        }

        private static BasicHttpBinding GetHttpBinding3()
        {
            var binding = new BasicHttpBinding
            {
                MaxReceivedMessageSize = 2147483647,
                ReaderQuotas = new XmlDictionaryReaderQuotas
                {
                    MaxStringContentLength = 2097152,
                    MaxArrayLength = 81920,
                    MaxBytesPerRead = 5120,
                    MaxDepth = 32,
                    MaxNameTableCharCount = 81920
                },
                CloseTimeout = TimeSpan.FromMinutes(10),
                OpenTimeout = TimeSpan.FromMinutes(10),
                ReceiveTimeout = TimeSpan.FromMinutes(10),
                SendTimeout = TimeSpan.FromMinutes(10),
                Security = new BasicHttpSecurity
                {
                    Mode = BasicHttpSecurityMode.TransportCredentialOnly,
                    Transport = new HttpTransportSecurity
                    {
                        ClientCredentialType = HttpClientCredentialType.Windows
                    },
                },
            };
            return binding;
        }

        public static LocalSessionAwareCoreServiceClient GetTcpClient(string host, string username, string password)
        {
            if (String.IsNullOrEmpty(host))
                host = "localhost";

            var binding = GetBinding();

            var endpoint = new EndpointAddress(String.Format("net.tcp://{0}:2660/CoreService/2012/netTcp", host));

            var client = new LocalSessionAwareCoreServiceClient(binding, endpoint);

            if (!String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(password))
            {
                client.ChannelFactory.Credentials.Windows.ClientCredential = new NetworkCredential(username, password);
            }

            return client;
        }

        public static LocalCoreServiceClient GetHttpClient(string host, string username, string password)
        {
            if (String.IsNullOrEmpty(host))
                host = "localhost";

            var binding = GetHttpBinding3();

            var endpoint = new EndpointAddress(String.Format("http://{0}/webservices/CoreService2012.svc/basicHttp", host));

            var client = new LocalCoreServiceClient(binding, endpoint);

            if (!String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(password))
            {
                client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
                client.ClientCredentials.Windows.ClientCredential = new NetworkCredential(username, password);
            }

            return client;
        }

        public static bool EnsureValidClient(string host, string username, string password)
        {
            if (Client == null || Client is SessionAwareCoreServiceClient && ((SessionAwareCoreServiceClient)Client).InnerChannel.State == CommunicationState.Faulted)
            {
                if (ClientBindingType == BindingType.HttpBinding)
                    Client = GetHttpClient(host, username, password);
                else
                    Client = GetTcpClient(host, username, password);

                try
                {
                    var publications = Client.GetSystemWideListXml(new PublicationsFilterData());
                }
                catch (SecurityNegotiationException)
                {
                    MessageBox.Show("Not able to connect to TCM. Check your credentials and try again.", "Login Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    return false;
                }
            }
            return true;
        }

        public static void ResetClient()
        {
            Client = null;
        }

        public static StreamDownloadClient GetStreamDownloadClient(string host, string username, string password)
        {
            if (String.IsNullOrEmpty(host))
                host = "localhost";

            var binding = GetHttpBinding2();

            var endpoint = new EndpointAddress(String.Format("http://{0}/webservices/CoreService2012.svc/streamDownload_basicHttp", host));

            StreamDownloadClient client = new StreamDownloadClient(binding, endpoint);

            if (!String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(password))
            {
                client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
                client.ClientCredentials.Windows.ClientCredential = new NetworkCredential(username, password);
            }

            return client;
        }

        private static void EnsureValidStreamDownloadClient(string host, string username, string password)
        {
            if (StreamDownloadClient == null || StreamDownloadClient.InnerChannel.State == CommunicationState.Faulted)
            {
                StreamDownloadClient = GetStreamDownloadClient(host, username, password);
            }
        }

        public static void ResetDownloadClient()
        {
            StreamDownloadClient = null;
        }

        public static StreamUploadClient GetStreamUploadClient(string host, string username, string password)
        {
            if (String.IsNullOrEmpty(host))
                host = "localhost";

            var binding = GetHttpBinding();

            var endpoint = new EndpointAddress(String.Format("http://{0}/webservices/CoreService2012.svc/streamUpload_basicHttp", host));

            StreamUploadClient client = new StreamUploadClient(binding, endpoint);

            return client;
        }

        private static void EnsureValidStreamUploadClient(string host, string username, string password)
        {
            if (StreamUploadClient == null || StreamUploadClient.InnerChannel.State == CommunicationState.Faulted)
            {
                StreamUploadClient = GetStreamUploadClient(host, username, password);
            }
        }

        public static void ResetUploadClient()
        {
            StreamUploadClient = null;
        }

        #endregion

        #region Tridion items access

        public static bool SaveRazorLayoutTbb(string host, string username, string password, string id, string code)
        {
            if (String.IsNullOrEmpty(id))
                return false;

            if (!EnsureValidClient(host, username, password))
                return false;

            TemplateBuildingBlockData tbbData = ReadItem(host, username, password, id) as TemplateBuildingBlockData;
            if (tbbData == null)
                return false;

            if (tbbData.BluePrintInfo.IsShared == true)
            {
                id = GetBluePrintTopTcmId(host, username, password, id);

                tbbData = ReadItem(host, username, password, id) as TemplateBuildingBlockData;
                if (tbbData == null)
                    return false;
            }

            try
            {
                tbbData = Client.CheckOut(id, true, new ReadOptions()) as TemplateBuildingBlockData;
            }
            catch (Exception)
            {
                return false;
            }

            if (tbbData == null)
                return false;

            tbbData.Content = code;

            try
            {
                tbbData = Client.Update(tbbData, new ReadOptions()) as TemplateBuildingBlockData;
                if (tbbData == null)
                    return false;

                if (tbbData.Content == code)
                {
                    Client.CheckIn(id, new ReadOptions());
                    return true;
                }

                Client.UndoCheckOut(id, true, new ReadOptions());
                return false;
            }
            catch (Exception)
            {
                Client.UndoCheckOut(id, true, new ReadOptions());
                return false;
            }
        }

        public static bool SaveRazorLayoutTbb(string host, string username, string password, string title, string code, string tcmContainer)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            if (ExistsItem(host, username, password, tcmContainer, title))
            {
                string id = GetItemTcmId(host, username, password, tcmContainer, title);
                if (String.IsNullOrEmpty(id))
                    return false;

                return SaveRazorLayoutTbb(host, username, password, id, code);
            }

            try
            {
                TemplateBuildingBlockData tbbData = new TemplateBuildingBlockData
                {
                    Content = code,
                    Title = title,
                    LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } },
                    Id = "tcm:0-0-0",
                    TemplateType = "RazorTemplate"
                };

                tbbData = Client.Save(tbbData, new ReadOptions()) as TemplateBuildingBlockData;
                if (tbbData == null)
                    return false;

                Client.CheckIn(tbbData.Id, new ReadOptions());
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static bool SavePageTemplate(string host, string username, string password, string title, string xml, string tcmContainer, string fileExtension)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            if (ExistsItem(host, username, password, tcmContainer, title))
            {
                string id = GetItemTcmId(host, username, password, tcmContainer, title);
                if (String.IsNullOrEmpty(id))
                    return false;

                PageTemplateData templateData = ReadItem(host, username, password, id) as PageTemplateData;
                if (templateData == null)
                    return false;

                if (templateData.BluePrintInfo.IsShared == true)
                {
                    id = GetBluePrintTopTcmId(host, username, password, id);

                    templateData = ReadItem(host, username, password, id) as PageTemplateData;
                    if (templateData == null)
                        return false;
                }

                try
                {
                    templateData = Client.CheckOut(id, true, new ReadOptions()) as PageTemplateData;
                }
                catch (Exception)
                {
                    return false;
                }

                if (templateData == null)
                    return false;

                templateData.Content = xml;
                templateData.Title = title;
                templateData.LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } };
                templateData.FileExtension = fileExtension;

                try
                {
                    templateData = Client.Update(templateData, new ReadOptions()) as PageTemplateData;
                    if (templateData == null)
                        return false;

                    if (templateData.Content == xml)
                    {
                        Client.CheckIn(id, new ReadOptions());
                        return true;
                    }

                    Client.UndoCheckOut(id, true, new ReadOptions());
                    return false;
                }
                catch (Exception)
                {
                    if (templateData == null)
                        return false;

                    Client.UndoCheckOut(templateData.Id, true, new ReadOptions());
                    return false;
                }
            }

            try
            {
                PageTemplateData templateData = new PageTemplateData
                {
                    Content = xml,
                    Title = title,
                    LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } },
                    Id = "tcm:0-0-0",
                    TemplateType = "CompoundTemplate",
                    FileExtension = fileExtension
                };

                templateData = Client.Save(templateData, new ReadOptions()) as PageTemplateData;
                if (templateData == null)
                    return false;

                Client.CheckIn(templateData.Id, new ReadOptions());
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static bool SaveComponentTemplate(string host, string username, string password, string title, string xml, string tcmContainer, string outputFormat, bool dynamic, params string[] allowedSchemaNames)
        {
            if (!EnsureValidClient(host, username, password))
                return false;
            
            List<LinkToSchemaData> schemaList = new List<LinkToSchemaData>();

            string tcmPublication = GetPublicationTcmId(tcmContainer);
            List<ItemInfo> allSchemas = GetSchemas(host, username, password, tcmPublication);

            if (allowedSchemaNames != null && allowedSchemaNames.Length > 0)
            {
                foreach (string schemaName in allowedSchemaNames)
                {
                    if (allSchemas.Any(x => x.Title == schemaName))
                    {
                        string tcmSchema = allSchemas.First(x => x.Title == schemaName).TcmId;
                        LinkToSchemaData link = new LinkToSchemaData {IdRef = tcmSchema};
                        schemaList.Add(link);
                    }
                }
            }

            if (ExistsItem(host, username, password, tcmContainer, title))
            {
                string id = GetItemTcmId(host, username, password, tcmContainer, title);
                if (String.IsNullOrEmpty(id))
                    return false;

                ComponentTemplateData templateData = ReadItem(host, username, password, id) as ComponentTemplateData;
                if (templateData == null)
                    return false;

                if (templateData.BluePrintInfo.IsShared == true)
                {
                    id = GetBluePrintTopTcmId(host, username, password, id);

                    templateData = ReadItem(host, username, password, id) as ComponentTemplateData;
                    if (templateData == null)
                        return false;
                }

                try
                {
                    templateData = Client.CheckOut(templateData.Id, true, new ReadOptions()) as ComponentTemplateData;
                }
                catch (Exception)
                {
                    return false;
                }

                if (templateData == null)
                    return false;

                templateData.Content = xml;
                templateData.Title = title;
                templateData.LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } };
                templateData.OutputFormat = outputFormat;
                templateData.RelatedSchemas = schemaList.ToArray();

                try
                {
                    templateData = Client.Update(templateData, new ReadOptions()) as ComponentTemplateData;
                    if (templateData == null)
                        return false;

                    if (templateData.Content == xml)
                    {
                        Client.CheckIn(templateData.Id, new ReadOptions());
                        return true;
                    }

                    Client.UndoCheckOut(templateData.Id, true, new ReadOptions());
                    return false;
                }
                catch (Exception)
                {
                    if (templateData == null)
                        return false;

                    Client.UndoCheckOut(templateData.Id, true, new ReadOptions());
                    return false;
                }
            }

            try
            {
                ComponentTemplateData templateData = new ComponentTemplateData
                {
                    Content = xml,
                    Title = title,
                    LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } },
                    Id = "tcm:0-0-0",
                    TemplateType = "CompoundTemplate",
                    OutputFormat = outputFormat,
                    IsRepositoryPublishable = dynamic,
                    AllowOnPage = true,
                    RelatedSchemas = schemaList.ToArray()
                };

                templateData = Client.Save(templateData, new ReadOptions()) as ComponentTemplateData;
                if (templateData == null)
                    return false;

                Client.CheckIn(templateData.Id, new ReadOptions());
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static bool SavePage(string host, string username, string password, string title, string fileName, string tcmContainer, string tcmPageTemplate, Dictionary<string, string> componentPresentations)
        {
            if (!EnsureValidClient(host, username, password))
                return false;
            
            tcmPageTemplate = ("start-" + tcmPageTemplate).Replace("start-" + (tcmPageTemplate.Split('-'))[0], (tcmContainer.Split('-'))[0]);

            List<ComponentPresentationData> componentPresentationsList = new List<ComponentPresentationData>();
            if (componentPresentations != null && componentPresentations.Count > 0)
            {
                foreach (string tcmComponent in componentPresentations.Keys)
                {
                    string tcmComponentTemplate = componentPresentations[tcmComponent];
                    ComponentPresentationData item = new ComponentPresentationData
                    {
                        Component = new LinkToComponentData { IdRef = tcmComponent },
                        ComponentTemplate = new LinkToComponentTemplateData { IdRef = tcmComponentTemplate }
                    };
                    componentPresentationsList.Add(item);
                }
            }

            if (ExistsItem(host, username, password, tcmContainer, title))
            {
                string id = GetItemTcmId(host, username, password, tcmContainer, title);
                if (String.IsNullOrEmpty(id))
                    return false;

                PageData pageData = ReadItem(host, username, password, id) as PageData;
                if (pageData == null)
                    return false;

                if (pageData.BluePrintInfo.IsShared == true)
                {
                    id = GetBluePrintTopTcmId(host, username, password, id);

                    pageData = ReadItem(host, username, password, id) as PageData;
                    if (pageData == null)
                        return false;
                }

                try
                {
                    pageData = Client.CheckOut(pageData.Id, true, new ReadOptions()) as PageData;
                }
                catch (Exception)
                {
                    return false;
                }

                if (pageData == null)
                    return false;

                pageData.Title = title;
                pageData.FileName = fileName;
                pageData.LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } };
                pageData.PageTemplate = new LinkToPageTemplateData { IdRef = tcmPageTemplate };
                pageData.ComponentPresentations = componentPresentationsList.ToArray();

                try
                {
                    pageData = Client.Update(pageData, new ReadOptions()) as PageData;
                    if (pageData == null)
                        return false;

                    Client.CheckIn(pageData.Id, new ReadOptions());
                    return true;
                }
                catch (Exception)
                {
                    if (pageData == null)
                        return false;

                    Client.UndoCheckOut(pageData.Id, true, new ReadOptions());
                    return false;
                }
            }

            try
            {
                PageData pageData = new PageData
                {
                    Title = title,
                    FileName = fileName,
                    LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } },
                    Id = "tcm:0-0-0",
                    IsPageTemplateInherited = false,
                    PageTemplate = new LinkToPageTemplateData { IdRef = tcmPageTemplate },
                    ComponentPresentations = componentPresentationsList.ToArray()
                };

                pageData = Client.Save(pageData, new ReadOptions()) as PageData;
                if (pageData == null)
                    return false;

                Client.CheckIn(pageData.Id, new ReadOptions());
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static bool CreateFolder(string host, string username, string password, string title, string tcmContainer)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            try
            {
                FolderData folderData = new FolderData
                {
                    Title = title,
                    LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } },
                    Id = "tcm:0-0-0"
                };

                folderData = Client.Save(folderData, new ReadOptions()) as FolderData;
                if (folderData == null)
                    return false;

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static string CreateFolderChain(string host, string username, string password, string folderPath, string tcmContainer)
        {
            if(String.IsNullOrEmpty(folderPath))
                return tcmContainer;

            if (String.IsNullOrEmpty(tcmContainer))
                return tcmContainer;

            string[] chain = folderPath.Trim('\\').Split('\\');
            if(chain.Length == 0)
                return tcmContainer;

            string topFolder = chain[0];
            List<ItemInfo> items = GetFoldersByParentFolder(host, username, password, tcmContainer);
            if (items.All(x => x.Title != topFolder))
            {
                CreateFolder(host, username, password, topFolder, tcmContainer);
                items = GetFoldersByParentFolder(host, username, password, tcmContainer);
            }

            string tcmTopFolder = items.First(x => x.Title == topFolder).TcmId;

            return CreateFolderChain(host, username, password, String.Join("\\", chain.Skip(1)), tcmTopFolder);
        }

        public static bool CreateStructureGroup(string host, string username, string password, string title, string tcmContainer)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            try
            {
                StructureGroupData sgData = new StructureGroupData
                {
                    Title = title,
                    Directory = title,
                    LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } },
                    Id = "tcm:0-0-0"
                };

                sgData = Client.Save(sgData, new ReadOptions()) as StructureGroupData;
                if (sgData == null)
                    return false;

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private static ComponentData GetComponent(string id)
        {
            if (String.IsNullOrEmpty(id))
                return null;

            return ReadItem(id) as ComponentData;
        }

        public static ComponentData GetComponent(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetComponent(id);
        }

        public static bool ExistsItem(string host, string username, string password, string tcmItem)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            return (ReadItem(tcmItem) != null);
        }
        
        public static bool ExistsItem(string tcmItem)
        {
            return (ReadItem(tcmItem) != null);
        }

        public static bool ExistsItem(string host, string username, string password, string tcmContainer, string itemTitle)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            if (String.IsNullOrEmpty(tcmContainer))
                return false;
            
            OrganizationalItemItemsFilterData filter = new OrganizationalItemItemsFilterData();
            return Client.GetList(tcmContainer, filter).Any(x => x.Title == itemTitle);
        }

        private static string GetItemTcmId(string tcmContainer, string itemTitle)
        {
            if (String.IsNullOrEmpty(tcmContainer))
                return String.Empty;

            OrganizationalItemItemsFilterData filter = new OrganizationalItemItemsFilterData();
            foreach (XElement element in Client.GetListXml(tcmContainer, filter).Nodes())
            {
                if (element.Attribute("Title").Value == itemTitle)
                    return element.Attribute("ID").Value;
            }

            return String.Empty;
        }

        public static string GetItemTcmId(string host, string username, string password, string tcmContainer, string itemTitle)
        {
            if (!EnsureValidClient(host, username, password))
                return String.Empty;

            return GetItemTcmId(tcmContainer, itemTitle);
        }

        public static IdentifiableObjectData ReadItem(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return null;
            
            return ReadItem(id);
        }

        private static IdentifiableObjectData ReadItem(string id)
        {
            try
            {
                return Client.Read(id, null);
            }
            catch (Exception)
            {
                return null;
            }
        }

        public static void SaveBinaryFromMultimediaComponent(string host, string username, string password, string id, string targetDir)
        {
            if (!EnsureValidClient(host, username, password))
                return;
            
            ComponentData multimediaComponent = Client.Read(id, new ReadOptions()) as ComponentData;
            if (multimediaComponent == null)
                return;

            string path = Path.Combine(targetDir, Path.GetFileName(multimediaComponent.BinaryContent.Filename));

            if (!Directory.Exists(targetDir))
                Directory.CreateDirectory(targetDir);

            EnsureValidStreamDownloadClient(host, username, password);

            using (Stream tempStream = StreamDownloadClient.DownloadBinaryContent(id))
            {
                using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
                {
                    byte[] binaryContent = null;

                    if (multimediaComponent.BinaryContent.FileSize != -1)
                    {
                        var memoryStream = new MemoryStream();
                        tempStream.CopyTo(memoryStream);
                        binaryContent = memoryStream.ToArray();
                    }
                    if (binaryContent == null)
                        return;

                    fs.Write(binaryContent, 0, binaryContent.Length);
                }
            }
        }

        public static BinaryContentData GetBinaryData(string host, string username, string password, string filePath)
        {
            EnsureValidStreamUploadClient(host, username, password);

            string title = Path.GetFileName(filePath);

            string tempLocation;
            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                tempLocation = StreamUploadClient.UploadBinaryContent(title, fs);
            }
            if (String.IsNullOrEmpty(tempLocation))
                return null;

            BinaryContentData binaryContent = new BinaryContentData();
            binaryContent.UploadFromFile = tempLocation;
            binaryContent.Filename = title;
            binaryContent.MultimediaType = new LinkToMultimediaTypeData { IdRef = GetMimeTypeId(host, username, password, filePath) };

            return binaryContent;
        }

        public static bool SaveMultimediaComponentFromBinary(string host, string username, string password, string id, string filePath)
        {
            if (String.IsNullOrEmpty(id))
                return false;

            if (!EnsureValidClient(host, username, password))
                return false;

            ComponentData multimediaComponent = ReadItem(host, username, password, id) as ComponentData;
            if (multimediaComponent == null)
                return false;

            if (multimediaComponent.BluePrintInfo.IsShared == true)
            {
                id = GetBluePrintTopTcmId(host, username, password, id);

                multimediaComponent = ReadItem(host, username, password, id) as ComponentData;
                if (multimediaComponent == null)
                    return false;
            }

            BinaryContentData binaryContent = GetBinaryData(host, username, password, filePath);
            if (binaryContent == null)
                return false;

            try
            {
                multimediaComponent = Client.CheckOut(id, true, new ReadOptions()) as ComponentData;
            }
            catch (Exception)
            {
                return false;
            }

            if (multimediaComponent == null)
                return false;

            multimediaComponent.BinaryContent = binaryContent;
            multimediaComponent.ComponentType = ComponentType.Multimedia;

            try
            {
                multimediaComponent = Client.Update(multimediaComponent, new ReadOptions()) as ComponentData;
                if (multimediaComponent == null)
                    return false;

                Client.CheckIn(id, new ReadOptions());
                return true;
            }
            catch (Exception)
            {
                if (multimediaComponent == null)
                    return false;

                Client.UndoCheckOut(multimediaComponent.Id, true, new ReadOptions());
                return false;
            }
        }

        public static bool SaveMultimediaComponentFromBinary(string host, string username, string password, string filePath, string title, string tcmContainer)
        {
            if (!File.Exists(filePath))
                return false;

            if (String.IsNullOrEmpty(title))
                title = Path.GetFileName(filePath);

            if (!EnsureValidClient(host, username, password))
                return false;

            if (ExistsItem(host, username, password, tcmContainer, title) || ExistsItem(host, username, password, tcmContainer, Path.GetFileNameWithoutExtension(filePath)))
            {
                string id = GetItemTcmId(host, username, password, tcmContainer, title);
                if (String.IsNullOrEmpty(id))
                    id = GetItemTcmId(host, username, password, tcmContainer, Path.GetFileNameWithoutExtension(filePath));

                if (String.IsNullOrEmpty(id))
                    return false;

                return SaveMultimediaComponentFromBinary(host, username, password, id, filePath);
            }

            try
            {
                BinaryContentData binaryContent = GetBinaryData(host, username, password, filePath);
                if (binaryContent == null)
                    return false;

                string tcmPublication = GetPublicationTcmId(tcmContainer);
                string schemaId = GetSchemas(host, username, password, tcmPublication).Single(x => x.Title == "Default Multimedia Schema").TcmId;

                // get namespace from component schema
                //SchemaData sd = Client.Read(schemaId, null) as SchemaData;
                //XNamespace ns = sd.NamespaceUri;

                //XElement metaXml = new XElement(ns + "Metadata");

                ComponentData multimediaComponent = new ComponentData
                {
                    Title = title,
                    LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = tcmContainer } },
                    Id = "tcm:0-0-0",
                    BinaryContent = binaryContent,
                    ComponentType = ComponentType.Multimedia,
                    Schema = new LinkToSchemaData { IdRef = schemaId },
                    //Metadata = metaXml.ToString(),
                    IsBasedOnMandatorySchema = false,
                    IsBasedOnTridionWebSchema = true,
                    ApprovalStatus = new LinkToApprovalStatusData
                    {
                        IdRef = "tcm:0-0-0"
                    }
                };

                multimediaComponent = Client.Save(multimediaComponent, new ReadOptions()) as ComponentData;
                if (multimediaComponent == null)
                    return false;

                Client.CheckIn(multimediaComponent.Id, new ReadOptions());
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        #endregion

        #region Tridion hierarchy

        public static List<ItemInfo> GetItemsByParentContainer(string host, string username, string password, string tcmContainer)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetItemsByParentContainer(tcmContainer);
        }

        private static List<ItemInfo> GetItemsByParentContainer(string tcmContainer)
        {
            return Client.GetListXml(tcmContainer, new OrganizationalItemItemsFilterData()).ToList();
        }

        public static List<ItemInfo> GetItemsByParentContainer(string host, string username, string password, string tcmContainer, bool recursive)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetItemsByParentContainer(tcmContainer, recursive);
        }

        private static List<ItemInfo> GetItemsByParentContainer(string tcmContainer, bool recursive)
        {
            return Client.GetListXml(tcmContainer, new OrganizationalItemItemsFilterData { Recursive = recursive }).ToList();
        }

        public static List<ItemInfo> GetFoldersByParentFolder(string host, string username, string password, string tcmFolder)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetFoldersByParentFolder(tcmFolder);
        }
        
        private static List<ItemInfo> GetFoldersByParentFolder(string tcmFolder)
        {
            return Client.GetListXml(tcmFolder, new OrganizationalItemItemsFilterData { ItemTypes = new[] { ItemType.Folder } }).ToList(ItemType.Folder);
        }

        public static List<ItemInfo> GetFolders(string host, string username, string password, string tcmFolder, bool recursive)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetFolders(tcmFolder, recursive);
        }

        private static List<ItemInfo> GetFolders(string tcmFolder, bool recursive)
        {
            return Client.GetListXml(tcmFolder, new OrganizationalItemItemsFilterData { ItemTypes = new[] { ItemType.Folder }, Recursive = recursive }).ToList(ItemType.Folder);
        }

        public static List<ItemInfo> GetTbbsByParentFolder(string host, string username, string password, string tcmFolder)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetTbbsByParentFolder(tcmFolder);
        }

        private static List<ItemInfo> GetTbbsByParentFolder(string tcmFolder)
        {
            return Client.GetListXml(tcmFolder, new OrganizationalItemItemsFilterData { ItemTypes = new[] { ItemType.TemplateBuildingBlock } }).ToList(ItemType.TemplateBuildingBlock);
        }

        public static List<ItemInfo> GetStructureGroupsByParentStructureGroup(string host, string username, string password, string tcmSG)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetStructureGroupsByParentStructureGroup(tcmSG);
        }

        private static List<ItemInfo> GetStructureGroupsByParentStructureGroup(string tcmSG)
        {
            return Client.GetListXml(tcmSG, new OrganizationalItemItemsFilterData { ItemTypes = new[] { ItemType.StructureGroup } }).ToList(ItemType.StructureGroup);
        }

        public static List<ItemInfo> GetFoldersByPublication(string host, string username, string password, string tcmPublication)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetFoldersByPublication(tcmPublication);
        }

        private static List<ItemInfo> GetFoldersByPublication(string tcmPublication)
        {
            return Client.GetListXml(tcmPublication, new RepositoryItemsFilterData { ItemTypes = new[] { ItemType.Folder } }).ToList(ItemType.Folder);
        }

        public static List<ItemInfo> GetStructureGroupsByPublication(string host, string username, string password, string tcmPublication)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetStructureGroupsByPublication(tcmPublication);
        }
        
        private static List<ItemInfo> GetStructureGroupsByPublication(string tcmPublication)
        {
            return Client.GetListXml(tcmPublication, new RepositoryItemsFilterData { ItemTypes = new[] { ItemType.StructureGroup } }).ToList(ItemType.StructureGroup);
        }

        public static List<ItemInfo> GetFoldersAndStructureGroupsByPublication(string host, string username, string password, string tcmPublication)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetFoldersAndStructureGroupsByPublication(tcmPublication);
        }

        private static List<ItemInfo> GetFoldersAndStructureGroupsByPublication(string tcmPublication)
        {
            List<ItemInfo> list = new List<ItemInfo>();
            List<ItemInfo> list1 = GetFoldersByPublication(tcmPublication);
            List<ItemInfo> list2 = GetStructureGroupsByPublication(tcmPublication);
            list.AddRange(list1);
            list.AddRange(list2);
            return list;
        }

        public static List<ItemInfo> GetItemsByPublication(string host, string username, string password, string tcmPublication, bool recursive)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetItemsByPublication(tcmPublication, recursive);
        }

        private static List<ItemInfo> GetItemsByPublication(string tcmPublication, bool recursive)
        {
            if (!recursive)
                return GetFoldersAndStructureGroupsByPublication(tcmPublication);

            List<ItemInfo> res = new List<ItemInfo>();

            foreach (ItemInfo container in GetFoldersAndStructureGroupsByPublication(tcmPublication))
            {
                res.Add(container);
                res.AddRange(GetItemsByParentContainer(container.TcmId, true));
            }

            return res;
        }

        public static List<ItemInfo> GetPublications(string host, string username, string password)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return Client.GetSystemWideListXml(new PublicationsFilterData()).ToList(ItemType.Publication);
        }

        public static List<ItemInfo> GetPublications(string host, string username, string password, string filterItemId)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            List<ItemInfo> publications = GetPublications(host, username, password);
            var allowedPublications = Client.GetSystemWideList(new BluePrintFilterData { ForItem = new LinkToRepositoryLocalObjectData { IdRef = filterItemId } }).Cast<BluePrintNodeData>().Where(x => x.Item != null).Select(x => GetPublicationTcmId(x.Item.Id)).ToList();
            return publications.Where(x => allowedPublications.Any(y => y == x.TcmId)).ToList();
        }

        public static string GetWebDav(this RepositoryLocalObjectData item)
        {
            string webDav = System.Web.HttpUtility.UrlDecode(item.LocationInfo.WebDavUrl.Replace("/webdav/", string.Empty));
            if (string.IsNullOrEmpty(webDav))
                return string.Empty;

            string fileExtension = Path.GetExtension(webDav);

            return string.IsNullOrEmpty(fileExtension) ? webDav : webDav.Replace(fileExtension, string.Empty);
        }

        private static List<string> GetUsingItems(string tcmItem, bool current, ItemType[] itemTypes)
        {
            UsingItemsFilterData filter = new UsingItemsFilterData();
            filter.IncludedVersions = current ? VersionCondition.OnlyLatestVersions : VersionCondition.AllVersions;
            filter.BaseColumns = ListBaseColumns.Id;
            if (itemTypes != null)
                filter.ItemTypes = itemTypes;

            List<string> items = Client.GetListXml(tcmItem, filter).ToList().Select(x => x.TcmId).ToList();
            return items;
        }

        private static List<string> GetUsingItems(string tcmItem)
        {
            return GetUsingItems(tcmItem, false, null);
        }

        private static List<string> GetUsingCurrentItems(string tcmItem)
        {
            return GetUsingItems(tcmItem, true, null);
        }

        private static List<string> GetUsedItems(string tcmItem, ItemType[] itemTypes)
        {
            UsedItemsFilterData filter = new UsedItemsFilterData();
            filter.BaseColumns = ListBaseColumns.Id;
            if (itemTypes != null)
                filter.ItemTypes = itemTypes;

            List<string> items = Client.GetListXml(tcmItem, filter).ToList().Select(x => x.TcmId).ToList();
            return items;
        }

        private static List<string> GetUsedItems(string tcmItem)
        {
            return GetUsedItems(tcmItem, null);
        }

        public static List<XElement> GetItemHistory(string host, string username, string password, string tcmItem)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetItemHistory(tcmItem);
        }

        public static List<XElement> GetItemHistory(string tcmItem)
        {
            VersionsFilterData versionsFilter = new VersionsFilterData();
            XElement listOfVersions = Client.GetListXml(tcmItem, versionsFilter);
            return listOfVersions.Descendants().ToList();
        }

        public static string GetItemFolder(string host, string username, string password, string tcmItem)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetItemFolder(tcmItem);
        }

        private static string GetItemFolder(string tcmItem)
        {
            RepositoryLocalObjectData item = Client.Read(tcmItem, new ReadOptions()) as RepositoryLocalObjectData;
            if (item == null)
                return string.Empty;

            return item.LocationInfo.OrganizationalItem.IdRef;
        }

        #endregion

        #region Tridion schemas

        public static List<ItemInfo> GetSchemas(string host, string username, string password, string tcmPublication)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetSchemas(tcmPublication);
        }

        private static List<ItemInfo> GetSchemas(string tcmPublication)
        {
            ItemInfo folder0 = GetFoldersByPublication(tcmPublication)[0];
            return Client.GetListXml(folder0.TcmId, new OrganizationalItemItemsFilterData { Recursive = true, ItemTypes = new[] { ItemType.Schema } }).ToList(ItemType.Schema);
        }

        public static List<ItemInfo> GetSchemas(string host, string username, string password, string tcmFolder, bool recursive)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetSchemas(tcmFolder, recursive);
        }

        private static List<ItemInfo> GetSchemas(string tcmFolder, bool recursive)
        {
            return Client.GetListXml(tcmFolder, new OrganizationalItemItemsFilterData { Recursive = recursive, ItemTypes = new[] { ItemType.Schema } }).ToList(ItemType.Schema);
        }

        public static List<ItemInfo> GetFoldersAndSchemasByParentFolder(string host, string username, string password, string tcmFolder)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            List<ItemInfo> list = new List<ItemInfo>();

            List<ItemInfo> listAllSchemas = GetSchemas(tcmFolder, true);
            if (listAllSchemas == null || listAllSchemas.Count == 0)
                return list;

            List<ItemInfo> listFolders = GetFoldersByParentFolder(tcmFolder);
            List<ItemInfo> listSchemas = GetSchemas(tcmFolder, false);
            list.AddRange(listFolders);
            list.AddRange(listSchemas);
            return list;
        }

        public static List<ItemFieldDefinitionData> GetSchemaFields(string host, string username, string password, string tcmSchema)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetSchemaFields(tcmSchema);
        }

        private static List<ItemFieldDefinitionData> GetSchemaFields(string tcmSchema)
        {
            SchemaFieldsData schemaFieldsData = Client.ReadSchemaFields(tcmSchema, false, null);
            if (schemaFieldsData == null || schemaFieldsData.Fields == null)
                return null;

            return schemaFieldsData.Fields.ToList();
        }

        public static List<ItemFieldDefinitionData> GetSchemaMetadataFields(string host, string username, string password, string tcmSchema)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetSchemaMetadataFields(tcmSchema);
        }

        private static List<ItemFieldDefinitionData> GetSchemaMetadataFields(string tcmSchema)
        {
            SchemaFieldsData schemaFieldsData = Client.ReadSchemaFields(tcmSchema, false, null);
            if (schemaFieldsData == null || schemaFieldsData.MetadataFields == null)
                return null;

            return schemaFieldsData.MetadataFields.ToList();
        }

        public static List<T> GetSchemaFields<T>(string host, string username, string password, string tcmSchema) where T : ItemFieldDefinitionData
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            SchemaFieldsData schemaFieldsData = Client.ReadSchemaFields(tcmSchema, false, null);
            return schemaFieldsData.Fields.Where(x => x is T).Cast<T>().ToList();
        }

        public static List<T> GetSchemaMetadataFields<T>(string host, string username, string password, string tcmSchema) where T : ItemFieldDefinitionData
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            SchemaFieldsData schemaFieldsData = Client.ReadSchemaFields(tcmSchema, false, null);
            return schemaFieldsData.MetadataFields.Where(x => x is T).Cast<T>().ToList();
        }

        public static List<ItemInfo> GetKeywordsByCategory(string host, string username, string password, string tcmCategory)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return Client.GetListXml(tcmCategory, new OrganizationalItemItemsFilterData { ItemTypes = new[] { ItemType.Keyword } }).ToList(ItemType.Keyword);
        }

        public static Schema GetSchemaData(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            SchemaData item;
            SchemaFieldsData schemaFields;

            try
            {
                item = Client.Read(id, new ReadOptions()) as SchemaData;
                schemaFields = Client.ReadSchemaFields(id, true, new ReadOptions());
            }
            catch (Exception ex)
            {
                throw ex;
            }

            Schema schema = new Schema();

            schema.Title = item.Title;
            schema.Description = item.Description;
            schema.RootElementName = item.RootElementName;
            schema.NamespaceUri = item.NamespaceUri;
            schema.LocationInfo = item.LocationInfo.Path;
            schema.SchemaType = item.Purpose.Value.ToString();

            schema.Fields = GetFieldsDescription(schemaFields.Fields);
            schema.MetadataFields = GetFieldsDescription(schemaFields.MetadataFields);

            return schema;
        }

        private const string FIELD_TYPE_TEXT = "Text";
        private const string FIELD_TYPE_MULTILINE_TEXT = "MultiLine Text";
        private const string FIELD_TYPE_RTF = "RTF";
        private const string FIELD_TYPE_NUMBER = "Number";
        private const string FIELD_TYPE_DATE = "Date";
        private const string FIELD_TYPE_COMPONENT_LINK = "Component Link";
        private const string FIELD_TYPE_MULTIMEDIA_LINK = "Multimedia Link";
        private const string FIELD_TYPE_EXTERNAL_LINK = "External Link";
        private const string FIELD_TYPE_EMBEDDED = "Embedded Schema";

        private const string TEXT_DEFAULT_VALUE = "Default Value: ";
        private const string TEXT_LIST_OF_VALUES = "List of values: ";
        private const string TEXT_VALUES_FROM_CATEGORY = "Values from Category: ";
        private const string TEXT_HEIGHT = "Height: ";
        private const string TEXT_ALLOWED_SCHEMAS = "Allowed Schemas: ";
        private const string TEXT_EMBEDDED_SCHEMA = "Embedded Schema: ";

        public static List<SchemaField> GetFieldsDescription(ItemFieldDefinitionData[] schemaFields)
        {
            List<SchemaField> fieldsDescription = new List<SchemaField>();

            if (schemaFields == null)
                return fieldsDescription;

            foreach (var field in schemaFields)
            {

                SchemaField newfield = new SchemaField();
                newfield.XmlName = field.Name;
                newfield.Required = field.IsMandatory();
                newfield.MultiValue = field.IsMultiValue();
                newfield.Description = field.Description;

                if (field is SingleLineTextFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_TEXT;
                    StringBuilder sb = new StringBuilder();

                    if (!String.IsNullOrEmpty(((SingleLineTextFieldDefinitionData)field).DefaultValue))
                        sb.Append(TEXT_DEFAULT_VALUE);
                    sb.Append(((SingleLineTextFieldDefinitionData)field).DefaultValue);

                    if (((SingleLineTextFieldDefinitionData)field).List != null && ((SingleLineTextFieldDefinitionData)field).List.Entries.Length > 0)
                    {
                        if (sb.Length != 0)
                            sb.Append(Environment.NewLine);
                        sb.Append(TEXT_LIST_OF_VALUES);

                        foreach (string entry in ((SingleLineTextFieldDefinitionData)field).List.Entries)
                        {
                            sb.Append(Environment.NewLine);
                            sb.Append(entry);
                        }
                    }

                    newfield.Properties = sb.ToString();
                }
                else if (field is KeywordFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_TEXT;
                    newfield.Properties = TEXT_VALUES_FROM_CATEGORY + ((KeywordFieldDefinitionData)field).Category.Title;
                }
                else if (field is MultiLineTextFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_MULTILINE_TEXT;

                    if (!String.IsNullOrEmpty(((MultiLineTextFieldDefinitionData)field).DefaultValue))
                        newfield.Properties = TEXT_DEFAULT_VALUE + ((MultiLineTextFieldDefinitionData)field).DefaultValue;

                    newfield.Properties += TEXT_HEIGHT + ((MultiLineTextFieldDefinitionData)field).Height;
                }
                else if (field is XhtmlFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_RTF;

                    if (!String.IsNullOrEmpty(((XhtmlFieldDefinitionData)field).DefaultValue))
                        newfield.Properties = TEXT_DEFAULT_VALUE + ((XhtmlFieldDefinitionData)field).DefaultValue;

                    newfield.Properties += TEXT_HEIGHT + ((XhtmlFieldDefinitionData)field).Height;
                }
                else if (field is NumberFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_NUMBER;

                    StringBuilder sb = new StringBuilder();

                    if (((NumberFieldDefinitionData)field).DefaultValue != null)
                    {
                        sb.Append(TEXT_DEFAULT_VALUE);
                        sb.Append(((NumberFieldDefinitionData)field).DefaultValue);
                    }

                    if (((NumberFieldDefinitionData)field).List != null && ((NumberFieldDefinitionData)field).List.Entries.Length > 0)
                    {
                        if (sb.Length != 0)
                            sb.Append(Environment.NewLine);

                        sb.Append(TEXT_LIST_OF_VALUES);

                        foreach (double entry in ((NumberFieldDefinitionData)field).List.Entries)
                        {
                            sb.Append(Environment.NewLine);
                            sb.Append(entry.ToString(CultureInfo.InvariantCulture));
                        }
                    }

                    newfield.Properties = sb.ToString();


                }
                else if (field is DateFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_DATE;
                    StringBuilder sb = new StringBuilder();

                    if (((DateFieldDefinitionData)field).DefaultValue != null)
                    {
                        sb.Append(TEXT_DEFAULT_VALUE);
                        sb.Append(((DateFieldDefinitionData)field).DefaultValue);
                    }

                    if (((DateFieldDefinitionData)field).List != null && ((DateFieldDefinitionData)field).List.Entries.Length > 0)
                    {
                        if (sb.Length != 0)
                            sb.Append(Environment.NewLine);

                        sb.Append(TEXT_LIST_OF_VALUES);

                        foreach (DateTime entry in ((DateFieldDefinitionData)field).List.Entries)
                        {
                            sb.Append(Environment.NewLine);
                            sb.Append(entry.ToString(CultureInfo.InvariantCulture));
                        }
                    }

                    newfield.Properties = sb.ToString();

                }

                else if (field is ComponentLinkFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_COMPONENT_LINK;
                    StringBuilder sb = new StringBuilder(TEXT_ALLOWED_SCHEMAS);

                    foreach (LinkToSchemaData schemaData in ((ComponentLinkFieldDefinitionData)field).AllowedTargetSchemas)
                    {
                        sb.Append(Environment.NewLine);
                        sb.Append(schemaData.Title);
                    }
                    newfield.Properties = sb.ToString();

                }
                else if (field is MultimediaLinkFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_MULTIMEDIA_LINK;
                    StringBuilder sb = new StringBuilder(TEXT_ALLOWED_SCHEMAS);
                    foreach (LinkToSchemaData schemaData in ((MultimediaLinkFieldDefinitionData)field).AllowedTargetSchemas)
                    {
                        sb.Append(Environment.NewLine);
                        sb.Append(schemaData.Title);
                    }
                    newfield.Properties = sb.ToString();
                }
                else if (field is ExternalLinkFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_EXTERNAL_LINK;
                    if (!String.IsNullOrEmpty(((ExternalLinkFieldDefinitionData)field).DefaultValue))
                        newfield.Properties = TEXT_DEFAULT_VALUE + ((ExternalLinkFieldDefinitionData)field).DefaultValue;

                }
                else if (field is EmbeddedSchemaFieldDefinitionData)
                {
                    newfield.FieldType = FIELD_TYPE_EMBEDDED;
                    newfield.Properties = TEXT_EMBEDDED_SCHEMA + ((EmbeddedSchemaFieldDefinitionData)field).EmbeddedSchema.Title;
                }

                fieldsDescription.Add(newfield);
            }

            return fieldsDescription;
        }

        #endregion

        #region Tridion components

        private static Dictionary<string, List<ItemInfo>> _cachedComponents;
        private static List<ItemInfo> GetComponents(string tcmSchema)
        {
            if (_cachedComponents != null && _cachedComponents.ContainsKey(tcmSchema))
            {
                return _cachedComponents[tcmSchema];
            }

            if(_cachedComponents == null)
                _cachedComponents = new Dictionary<string, List<ItemInfo>>();

            List<ItemInfo> res = Client.GetListXml(tcmSchema, new UsingItemsFilterData { ItemTypes = new[] { ItemType.Component } }).ToList(ItemType.Component);

            _cachedComponents.Add(tcmSchema, res);

            return res;
        }

        public static List<ItemInfo> GetComponents(string host, string username, string password, string tcmSchema)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetComponents(tcmSchema);
        }

        private static List<ItemInfo> GetComponents(string tcmFolder, bool recursive)
        {
            return Client.GetListXml(tcmFolder, new OrganizationalItemItemsFilterData { ItemTypes = new[] { ItemType.Component }, Recursive = recursive }).ToList(ItemType.Component);
        }

        public static List<ItemInfo> GetComponents(string host, string username, string password, string tcmFolder, bool recursive)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetComponents(tcmFolder, recursive);
        }

        public static List<ItemInfo> GetFoldersAndComponentsByParentFolder(string host, string username, string password, string tcmFolder)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            List<ItemInfo> list = new List<ItemInfo>();

            List<ItemInfo> listAllComponenets = GetComponents(tcmFolder, true);
            if (listAllComponenets == null || listAllComponenets.Count == 0)
                return list;
            
            List<ItemInfo> listFolders = GetFoldersByParentFolder(tcmFolder);
            List<ItemInfo> listComponents = GetComponents(tcmFolder, false);
            list.AddRange(listFolders);
            list.AddRange(listComponents);
            return list;
        }

        private static List<ItemInfo> GetComponentsByTextCriterias(string tcmFolder, string text, string tcmSchema, List<Criteria> textCriterias)
        {
            if ((textCriterias == null || textCriterias.Count == 0) && string.IsNullOrEmpty(text))
                return new List<ItemInfo>();

            SearchQueryData filter = new SearchQueryData();
            filter.ItemTypes = new[] { ItemType.Component };

            if (!String.IsNullOrEmpty(tcmFolder))
                filter.SearchIn = new LinkToIdentifiableObjectData { IdRef = tcmFolder };

            if (!String.IsNullOrEmpty(text))
                filter.FullTextQuery = text;

            if (!String.IsNullOrEmpty(tcmSchema) && textCriterias != null && textCriterias.Count > 0)
            {
                List<BasedOnSchemaData> basedOnSchemas = new List<BasedOnSchemaData>();
                foreach (Criteria creteria in textCriterias)
                {
                    BasedOnSchemaData basedSchema = new BasedOnSchemaData();
                    basedSchema.Schema = new LinkToSchemaData { IdRef = tcmSchema };
                    basedSchema.Field = creteria.Field.Name;

                    if (creteria.Operation == Operation.Equal)
                        basedSchema.FieldValue = creteria.Value.ToString();
                    
                    if (creteria.Operation == Operation.Like)
                        basedSchema.FieldValue = "*" + creteria.Value + "*";

                    basedOnSchemas.Add(basedSchema);
                }

                filter.BasedOnSchemas = basedOnSchemas.ToArray();
            }

            return Client.GetSearchResultsXml(filter).ToList(ItemType.Component);
        }

        public static List<ItemInfo> GetComponentsByTextCriterias(string host, string username, string password, string tcmFolder, string text, string tcmSchema, List<Criteria> creterias)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetComponentsByTextCriterias(tcmFolder, text, tcmSchema, creterias);
        }

        private static List<ItemInfo> GetComponentsByDateOrNumberCriterias(string tcmSchema, List<Criteria> creterias)
        {
            if (creterias == null || creterias.Count == 0)
                return new List<ItemInfo>();

            List<List<ItemInfo>> arrRes = new List<List<ItemInfo>>();

            foreach (Criteria creteria in creterias)
            {
                List<ItemInfo> criteriaRes = GetComponentsByDateOrNumberSingleCriteria(tcmSchema, creteria);
                arrRes.Add(criteriaRes);
            }

            return Intersect(arrRes.ToArray(), true);
        }

        private static List<ItemInfo> GetComponentsByDateOrNumberSingleCriteria(string tcmSchema, Criteria creteria)
        {
            List<ItemInfo> res = new List<ItemInfo>();

            List<ItemInfo> components = GetComponents(tcmSchema);

            foreach (ItemInfo item in components)
            {
                ComponentData component = Client.Read(item.TcmId, new ReadOptions()) as ComponentData;
                if (component == null)
                    continue;

                var schemaFields = Client.ReadSchemaFields(component.Schema.IdRef, false, null);
                XNamespace ns = schemaFields.NamespaceUri;

                object value = GetComponentSingleValue(component, creteria.Field, ns);
                if (value == null)
                    continue;

                if (creteria.Field.IsDate() && creteria.Value != null)
                {
                    DateTime date = (DateTime) value;
                    DateTime compareDate = (DateTime) creteria.Value;

                    if (creteria.Operation == Operation.Equal && date == compareDate ||
                        creteria.Operation == Operation.Greater && date > compareDate ||
                        creteria.Operation == Operation.Less && date < compareDate)
                        res.Add(item);
                }

                if (creteria.Field.IsNumber() && creteria.Value != null)
                {
                    double num = (double) value;
                    double compareNum = (double) creteria.Value;

                    if (creteria.Operation == Operation.Equal && Math.Abs(num - compareNum) < 0.001 ||
                        creteria.Operation == Operation.Greater && num > compareNum ||
                        creteria.Operation == Operation.Less && num < compareNum)
                        res.Add(item);
                }
            }

            return res;
        }

        public static List<ItemInfo> GetComponentsByDateOrNumberCriterias(string host, string username, string password, string tcmSchema, List<Criteria> creterias)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetComponentsByDateOrNumberCriterias(tcmSchema, creterias);
        }

        public static string GetComponentXml(SchemaData schema, string rootElementName, List<ComponentFieldData> componentFieldDataValues)
        {
            XNamespace ns = schema.NamespaceUri;
            XElement contentXml = new XElement(ns + rootElementName);

            foreach (ComponentFieldData componentFieldDataValue in componentFieldDataValues)
            {
                if (componentFieldDataValue.IsMultiValue)
                {
                    IEnumerable values = componentFieldDataValue.Value as IEnumerable;
                    if (values != null)
                    {
                        foreach (object value in values)
                        {
                            if (value is XElement)
                            {
                                contentXml.Add(value);
                            }
                            else
                            {
                                contentXml.Add(new XElement(ns + componentFieldDataValue.SchemaField.Name, value));
                            }
                        }
                    }
                }
                else
                {
                    if (componentFieldDataValue.Value is XElement)
                    {
                        contentXml.Add(componentFieldDataValue.Value);
                    }
                    else
                    {
                        contentXml.Add(new XElement(ns + componentFieldDataValue.SchemaField.Name, componentFieldDataValue.Value));
                    }
                }
            }

            return contentXml.ToString();
        }

        public static object GetComponentFieldData(this XElement element, ItemFieldDefinitionData schemaField)
        {
            if (element == null)
                return null;

            string value = element.GetInnerXml();

            if (schemaField.IsNumber())
                return String.IsNullOrEmpty(value) ? null : (double?)double.Parse(value);

            if (schemaField.IsDate())
                return String.IsNullOrEmpty(value) ? null : (DateTime?)DateTime.Parse(value);

            if (schemaField.IsText())
                return value;

            return element;
        }

        public static object GetComponentFieldData(string value, ItemFieldDefinitionData schemaField)
        {
            if (String.IsNullOrEmpty(value))
                return null;

            if (schemaField.IsNumber())
                return String.IsNullOrEmpty(value) ? null : (double?)double.Parse(value);

            if (schemaField.IsDate())
                return String.IsNullOrEmpty(value) ? null : (DateTime?)DateTime.Parse(value);

            return value;
        }

        public static object GetComponentSingleValue(ComponentData componentData, ItemFieldDefinitionData schemaField, XNamespace ns)
        {
            if (componentData == null)
                return null;

            XDocument doc = XDocument.Parse(componentData.Content);
            XElement element = doc.Root.Element(ns + schemaField.Name);

            if (element == null && !string.IsNullOrEmpty(componentData.Metadata))
            {
                XDocument docMeta = XDocument.Parse(componentData.Metadata);
                element = docMeta.Root.Element(ns + schemaField.Name);
            }

            return element.GetComponentFieldData(schemaField);
        }

        public static List<FieldMappingInfo> GetDefaultFieldMapping(string host, List<ItemFieldDefinitionData> sourceSchemaFields, List<ItemFieldDefinitionData> targetSchemaFields, string targetSchemaUri)
        {
            List<FieldMappingInfo> fieldMapping = new List<FieldMappingInfo>();
            foreach (ItemFieldDefinitionData targetSchemaLink in targetSchemaFields)
            {
                string sourceFieldName = GetFromIsolatedStorage(host + "_" + targetSchemaUri.Replace("tcm:", "") + "_" + targetSchemaLink.Name);
                if (string.IsNullOrEmpty(sourceFieldName))
                    sourceFieldName = targetSchemaLink.Name;
                
                ItemFieldDefinitionData sourceField = sourceFieldName == "< ignore >" ? null : sourceSchemaFields.FirstOrDefault(x => x.Name == sourceFieldName) ?? targetSchemaLink;

                string defaultValue = GetFromIsolatedStorage(host + "_" + targetSchemaUri.Replace("tcm:", "") + "_" + targetSchemaLink.Name + "_DefaultValue");

                fieldMapping.Add(new FieldMappingInfo { SourceField = sourceField, TargetField = targetSchemaLink, SourceSchemaFields = sourceSchemaFields, DefaultValue = defaultValue });
            }

            return fieldMapping;
        }

        public static List<FieldMappingInfo> GetDefaultFieldMapping(List<ItemFieldDefinitionData> targetSchemaFields, string targetSchemaUri)
        {
            List<FieldMappingInfo> fieldMapping = new List<FieldMappingInfo>();
            foreach (ItemFieldDefinitionData targetSchemaLink in targetSchemaFields)
            {
                fieldMapping.Add(new FieldMappingInfo { SourceField = targetSchemaLink, TargetField = targetSchemaLink });
            }

            return fieldMapping;
        }

        private static ResultInfo SaveComponent(SchemaData schema, string title, string contentXml, string metadataXml, string folderUri)
        {
            ResultInfo result = new ResultInfo();
            result.ItemType = ItemType.Component;

            //check existing item
            List<ItemInfo> targetFolderItems = GetItemsByParentContainer(folderUri);
            if (targetFolderItems.All(x => x.Title != title))
            {
                //create new component
                try
                {
                    ComponentData component = new ComponentData
                    {
                        Title = title,
                        LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = folderUri } },
                        Id = "tcm:0-0-0",
                        Schema = new LinkToSchemaData { IdRef = schema.Id },
                        Content = contentXml,
                        Metadata = metadataXml,
                        IsBasedOnMandatorySchema = false,
                        IsBasedOnTridionWebSchema = true,
                        ApprovalStatus = new LinkToApprovalStatusData { IdRef = "tcm:0-0-0" }
                    };

                    component = Client.Save(component, new ReadOptions()) as ComponentData;

                    if (component != null)
                        Client.CheckIn(component.Id, new ReadOptions());

                    component = GetComponent(GetItemTcmId(folderUri, title));

                    if (component != null)
                    {
                        result.TcmId = component.Id;
                        result.Status = Status.Success;
                        result.Message = String.Format("Created component \"{0}\"", component.GetWebDav().CutPath("/", 80, true));
                    }
                }
                catch (Exception ex)
                {
                    result.TcmId = folderUri;
                    result.Status = Status.Error;
                    result.StackTrace = ex.StackTrace;
                    result.Message = String.Format("Error creating component \"{0}\"", title);
                }
            }
            else
            {
                //update existing component
                string id = targetFolderItems.First(x => x.Title == title).TcmId;
                
                result.TcmId = id;

                ComponentData component = GetComponent(id);

                //only component of same name and title
                if (component != null && component.Schema.IdRef == schema.Id)
                {
                    try
                    {
                        component = (ComponentData)Client.CheckOut(id, true, new ReadOptions());
                    }
                    catch (Exception)
                    {
                    }

                    component.Content = contentXml;
                    component.Metadata = metadataXml;

                    try
                    {
                        Client.Update(component, new ReadOptions());
                        Client.CheckIn(id, new ReadOptions());

                        result.Status = Status.Success;
                        result.Message = String.Format("Updated component \"{0}\"", component.GetWebDav().CutPath("/", 80, true));
                    }
                    catch (Exception ex)
                    {
                        Client.UndoCheckOut(id, true, new ReadOptions());

                        result.Status = Status.Error;
                        result.StackTrace = ex.StackTrace;
                        result.Message = String.Format("Error updating component \"{0}\"", component.GetWebDav().CutPath("/", 80, true));
                    }
                }
                else
                {
                    result.TcmId = folderUri;
                    result.Status = Status.Error;
                    result.Message = String.Format("Error updating component \"{0}\"", title);
                }
            }

            return result;
        }

        public static XElement GetComponentLink(string id, string title, string rootElementName)
        {
            XNamespace ns = "http://www.w3.org/1999/xlink";

            return new XElement(rootElementName,
                new XAttribute(XNamespace.Xmlns + "xlink", ns),
                new XAttribute(ns + "href", id),
                new XAttribute(ns + "title", title));
        }

        public static XElement EmbeddedSchemaToComponentLink(this XElement sourceElement, ComponentLinkFieldDefinitionData targetField, string targetFolderUri, string sourceTcmId, List<FieldMappingInfo> childFieldMapping, int index, List<ResultInfo> results)
        {
            if (sourceElement == null || targetField == null)
                return null;

            if (!targetField.AllowedTargetSchemas.Any())
                return null;

            string targetSchemaUri = targetField.AllowedTargetSchemas[0].IdRef;

            SchemaData targetSchema = Client.Read(targetSchemaUri, null) as SchemaData;
            if (targetSchema == null)
                return null;

            List<ItemFieldDefinitionData> targetSchemaFields = GetSchemaFields(targetSchemaUri);

            string xml = sourceElement.ToString();

            //get fixed xml
            string newXml = GetComponentFixedContent(targetSchema, targetSchema.RootElementName, targetSchemaFields, xml, sourceElement.GetDefaultNamespace(), sourceTcmId, childFieldMapping, results);

            if (string.IsNullOrEmpty(newXml))
                return null;

            ComponentData sourceComponent = GetComponent(sourceTcmId);

            string title = string.Format("[{0:00}0] {1}", index, sourceComponent.Title);

            ResultInfo result = SaveComponent(targetSchema, title, newXml, string.Empty, targetFolderUri);

            results.Add(result);

            return GetComponentLink(result.TcmId, title, targetField.Name);
        }

        public static XElement ComponentLinkToEmbeddedSchema(this XElement sourceElement, EmbeddedSchemaFieldDefinitionData targetField, List<FieldMappingInfo> childFieldMapping, List<ResultInfo> results)
        {
            XNamespace ns = "http://www.w3.org/1999/xlink";
            string sourceComponentUri = sourceElement.Attribute(ns + "href").Value;

            ComponentData sourceComponent = Client.Read(sourceComponentUri, new ReadOptions()) as ComponentData;
            if (sourceComponent == null || string.IsNullOrEmpty(sourceComponent.Content))
                return null;

            string sourceSchemaUri = sourceComponent.Schema.IdRef;
            SchemaData sourceSchema = Client.Read(sourceSchemaUri, null) as SchemaData;
            if (sourceSchema == null)
                return null;
           
            string targetEmbeddedSchemaUri = targetField.EmbeddedSchema.IdRef;
            SchemaData targetEmbeddedSchema = Client.Read(targetEmbeddedSchemaUri, null) as SchemaData;
            if (targetEmbeddedSchema == null)
                return null;

            List<ItemFieldDefinitionData> targetEmbeddedSchemaFields = GetSchemaFields(targetEmbeddedSchemaUri);

            string xml = sourceComponent.Content;

            //get fixed xml
            string newXml = targetEmbeddedSchemaFields.Count == 0 ? string.Empty : GetComponentFixedContent(targetEmbeddedSchema, targetField.Name, targetEmbeddedSchemaFields, xml, sourceSchema.NamespaceUri, sourceComponentUri, childFieldMapping, results);

            if (string.IsNullOrEmpty(newXml))
                return null;

            return XElement.Parse(newXml);
        }

        public static XElement EmbeddedSchemaToEmbeddedSchema(this XElement sourceElement, EmbeddedSchemaFieldDefinitionData sourceField, EmbeddedSchemaFieldDefinitionData targetField, List<FieldMappingInfo> childFieldMapping, List<ResultInfo> results)
        {
            if (sourceElement == null || targetField == null)
                return null;

            string targetEmbeddedSchemaUri = targetField.EmbeddedSchema.IdRef;
            SchemaData targetEmbeddedSchema = Client.Read(targetEmbeddedSchemaUri, null) as SchemaData;
            if (targetEmbeddedSchema == null)
                return null;

            List<ComponentFieldData> targetFieldDataValues = new List<ComponentFieldData>();

            foreach (FieldMappingInfo mapping in childFieldMapping)
            {
                if (mapping.SourceField != null && mapping.SourceField.Name == "< parent source >")
                {
                    ComponentFieldData item = new ComponentFieldData();
                    item.SchemaField = sourceField;
                    item.Value = sourceElement;
                    targetFieldDataValues.Add(item);
                }
                else if (!string.IsNullOrEmpty(mapping.DefaultValue))
                {
                    ComponentFieldData item = new ComponentFieldData();
                    item.SchemaField = mapping.TargetField;
                    item.Value = mapping.DefaultValue;
                    targetFieldDataValues.Add(item);
                }
            }

            string xml = GetComponentXml(targetEmbeddedSchema, targetField.Name, targetFieldDataValues);
            xml = xml.Replace(sourceElement.GetDefaultNamespace().ToString(), targetEmbeddedSchema.NamespaceUri);
            xml = xml.Replace(" xmlns=\"\"", string.Empty);

            if (string.IsNullOrEmpty(xml))
                return null;

            return XElement.Parse(xml);
        }

        public static List<ComponentFieldData> GetComponentValues(SchemaData targetSchema, string targetRootElementName, List<ItemFieldDefinitionData> targetSchemaFields, string sourceXml, XNamespace sourceNs, string sourceTcmId, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            XDocument doc = XDocument.Parse(sourceXml);
            if (doc.Root == null)
                return null;

            List<ComponentFieldData> res = new List<ComponentFieldData>();

            foreach (ItemFieldDefinitionData field in targetSchemaFields)
            {
                string targetName = field.Name;
                string sourceName = fieldMapping != null && fieldMapping.Any(x => x.TargetField.Name == targetName) ? fieldMapping.First(x => x.TargetField.Name == targetName).SourceField.Name : targetName;

                if (!string.IsNullOrEmpty(sourceName) && doc.Root.Element(sourceNs + sourceName) != null)
                {
                    List<XElement> elements = doc.Root.Elements(sourceNs + sourceName).ToList();
                    
                    ComponentFieldData item = new ComponentFieldData();
                    item.SchemaField = field;

                    FieldMappingInfo mapping = fieldMapping == null ? null : fieldMapping.FirstOrDefault(x => x.TargetField.Name == targetName);
                    
                    //transform Embedded Schema into Component Link
                    if (mapping != null && mapping.SourceField.IsEmbedded() && mapping.TargetField.IsComponentLink())
                    {
                        if (!field.IsMultiValue())
                        {
                            item.Value = (elements.FirstOrDefault().GetComponentFieldData(field) as XElement).EmbeddedSchemaToComponentLink(field as ComponentLinkFieldDefinitionData, mapping.TargetFolderUri, sourceTcmId, mapping.ChildFieldMapping, 0, results);
                        }
                        else
                        {
                            List<XElement> values = new List<XElement>();
                            int index = 0;
                            foreach (XElement element in elements)
                            {
                                index ++;
                                values.Add((element.GetComponentFieldData(field) as XElement).EmbeddedSchemaToComponentLink(field as ComponentLinkFieldDefinitionData, mapping.TargetFolderUri, sourceTcmId, mapping.ChildFieldMapping, index, results));
                            }
                            item.Value = values;
                        }
                    }

                    //transform Component Link into Embedded Schema
                    else if (mapping != null && mapping.SourceField.IsComponentLink() && mapping.TargetField.IsEmbedded())
                    {
                        if (!field.IsMultiValue())
                        {
                            item.Value = (elements.FirstOrDefault().GetComponentFieldData(field) as XElement).ComponentLinkToEmbeddedSchema(field as EmbeddedSchemaFieldDefinitionData, mapping.ChildFieldMapping, results);
                        }
                        else
                        {
                            item.Value = elements.Select(x => (x.GetComponentFieldData(field) as XElement).ComponentLinkToEmbeddedSchema(field as EmbeddedSchemaFieldDefinitionData, mapping.ChildFieldMapping, results)).ToList();
                        }
                    }

                    //transform Embedded Schema into another Embedded Schema
                    else if (mapping != null && mapping.SourceField.IsEmbedded() && mapping.TargetField.IsEmbedded() && (((EmbeddedSchemaFieldDefinitionData)mapping.SourceField).EmbeddedSchema.IdRef != ((EmbeddedSchemaFieldDefinitionData)mapping.TargetField).EmbeddedSchema.IdRef))
                    {
                        if (!field.IsMultiValue())
                        {
                            item.Value = (elements.FirstOrDefault().GetComponentFieldData(field) as XElement).EmbeddedSchemaToEmbeddedSchema((EmbeddedSchemaFieldDefinitionData)mapping.SourceField, field as EmbeddedSchemaFieldDefinitionData, mapping.ChildFieldMapping, results);
                        }
                        else
                        {
                            item.Value = elements.Select(x => (x.GetComponentFieldData(field) as XElement).EmbeddedSchemaToEmbeddedSchema((EmbeddedSchemaFieldDefinitionData)mapping.SourceField, field as EmbeddedSchemaFieldDefinitionData, mapping.ChildFieldMapping, results)).ToList();
                        }
                    }

                    //same field type
                    else
                    {
                        if (!field.IsMultiValue())
                        {
                            item.Value = elements.FirstOrDefault().GetComponentFieldData(field);
                        }
                        else
                        {
                            item.Value = elements.Select(x => x.GetComponentFieldData(field)).ToList();
                        }
                    }

                    res.Add(item);
                }
            }

            return res;
        }

        public static string GetComponentFixedContent(SchemaData targetSchema, string targetRootElementName, List<ItemFieldDefinitionData> targetSchemaFields, string sourceXml, XNamespace sourceNs, string sourceTcmId, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            bool success = true;

            List<ComponentFieldData> componentFieldDataValues = GetComponentValues(targetSchema, targetRootElementName, targetSchemaFields, sourceXml, sourceNs, sourceTcmId, fieldMapping, results);

            if (fieldMapping == null)
                fieldMapping = GetDefaultFieldMapping(targetSchemaFields, targetSchema.Id);

            //set default values from mapping
            foreach (FieldMappingInfo mapping in fieldMapping)
            {
                object defaultValue = GetComponentFieldData(mapping.DefaultValue, mapping.TargetField);
                if (defaultValue != null)
                {
                    ComponentFieldData componentFieldDataValue = componentFieldDataValues.FirstOrDefault(x => x.SchemaField.Name == mapping.TargetField.Name);
                    if (componentFieldDataValue != null)
                    {
                        if (componentFieldDataValue.Value == null)
                            componentFieldDataValue.Value = defaultValue;
                    }
                    else
                    {
                        componentFieldDataValue = new ComponentFieldData();
                        componentFieldDataValue.SchemaField = mapping.TargetField;
                        componentFieldDataValue.Value = defaultValue;
                        componentFieldDataValues.Add(componentFieldDataValue);
                    }
                }
            }

            //check mandatory and empty items
            foreach (ItemFieldDefinitionData schemaField in targetSchemaFields)
            {
                if (schemaField.IsMandatory())
                {
                    ComponentFieldData componentFieldDataValue = componentFieldDataValues.FirstOrDefault(x => x.SchemaField.Name == schemaField.Name);
                    if (componentFieldDataValue == null || componentFieldDataValue.Value == null)
                    {
                        success = false;

                        ResultInfo result = new ResultInfo();
                        result.ItemType = ItemType.Component;
                        result.TcmId = sourceTcmId;
                        result.Status = Status.Error;
                        ComponentData component = GetComponent(sourceTcmId);
                        result.Message = String.Format("Component \"{0}\" contains mandatory empty fields. Please change mapping.", component == null ? sourceTcmId : component.GetWebDav().CutPath("/", 90, true));
                        results.Add(result);
                    }
                }
            }
            if (!success)
                return string.Empty;

            string res = GetComponentXml(targetSchema, targetRootElementName, componentFieldDataValues);

            //replace to local publication ids
            List<string> ids = Regex.Matches(res, "tcm:(\\d)+-(\\d)+(-(\\d)+)?").Cast<Match>().Select(x => x.Value).ToList();
            foreach (string id in ids)
            {
                string newId = Regex.Replace(id, "tcm:(\\d)+-", targetSchema.Id.Split('-')[0] + "-");
                if (ExistsItem(newId))
                {
                    res = res.Replace(id, newId);
                }
                else
                {
                    success = false;

                    ResultInfo result = new ResultInfo();
                    result.ItemType = ItemType.Component;
                    result.TcmId = id;
                    result.Status = Status.Error;
                    ComponentData component = GetComponent(id);
                    result.Message = String.Format("Component \"{0}\" doesn't exist in target publication", component == null ? id : component.GetWebDav().CutPath("/", 90, true));
                    results.Add(result);
                }
            }
            if (!success)
                return string.Empty;

            //clear unnecessary namespaces
            res = res.Replace(" xmlns=\"\"", string.Empty);
            res = res.Replace(String.Format(" xmlns=\"{0}\"", targetSchema.NamespaceUri), string.Empty);
            res = res.Replace(String.Format("<{0}", targetRootElementName), String.Format("<{0} xmlns=\"{1}\"", targetRootElementName, targetSchema.NamespaceUri));

            res = res.Replace(sourceNs.ToString(), targetSchema.NamespaceUri);

            return res;
        }

        public static void ChangeSchemaForComponent(string host, string username, string password, string componentUri, string sourceSchemaUri, string targetSchemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            // Open up the source schema 
            SchemaData sourceSchema = Client.Read(sourceSchemaUri, null) as SchemaData;
            if (sourceSchema == null)
                return;

            // Open up the schema that we will be changing to
            SchemaData targetSchema = Client.Read(targetSchemaUri, null) as SchemaData;
            if (targetSchema == null)
                return;

            List<ItemFieldDefinitionData> targetSchemaFields = GetSchemaFields(targetSchemaUri);
            List<ItemFieldDefinitionData> targetSchemaMetadataFields = GetSchemaMetadataFields(targetSchemaUri);

            // Change schema for component
            ChangeSchemaForComponent(componentUri, sourceSchema, targetSchema, targetSchemaFields, targetSchemaMetadataFields, fieldMapping, results);
        }

        private static void ChangeSchemaForComponent(string componentUri, SchemaData sourceSchema, SchemaData targetSchema, List<ItemFieldDefinitionData> targetSchemaFields, List<ItemFieldDefinitionData> targetSchemaMetadataFields, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            if (string.IsNullOrEmpty(componentUri))
                return;

            ComponentData component = Client.Read(componentUri, new ReadOptions()) as ComponentData;
            if (component == null)
                return;

            if (!component.Schema.IdRef.Equals(sourceSchema.Id))
            {
                // If the component is not of the schmea that we want to change from, do nothing...
                return;
            }

            if (component.Schema.IdRef.Equals(targetSchema.Id))
            {
                // If the component already has this schema, don't do anything.
                return;
            }

            ResultInfo result = new ResultInfo();
            result.ItemType = ItemType.Component;
            result.TcmId = component.Id;

            //get fixed xml
            string newContent = targetSchemaFields.Count == 0 ? string.Empty : GetComponentFixedContent(targetSchema, targetSchema.RootElementName, targetSchemaFields, component.Content, sourceSchema.NamespaceUri, componentUri, fieldMapping, results);
            string newMetadata = targetSchemaMetadataFields.Count == 0 ? string.Empty : GetComponentFixedContent(targetSchema, "Metadata", targetSchemaMetadataFields, component.Metadata, sourceSchema.NamespaceUri, componentUri, fieldMapping, results);

            if (string.IsNullOrEmpty(newContent))
                return;

            component = Client.TryCheckOut(component.Id, new ReadOptions()) as ComponentData;

            if (component.IsEditable.Value)
            {
                try
                {
                    //rebild component xml
                    component.Content = newContent;

                    //rebuild metadata
                    component.Metadata = newMetadata;

                    //change schema id
                    component.Schema.IdRef = targetSchema.Id;

                    Client.Save(component, new ReadOptions());
                    Client.CheckIn(component.Id, new ReadOptions());

                    result.Status = Status.Success;
                    result.Message = String.Format("Changed schema for \"{0}\"", component.GetWebDav().CutPath("/", 80, true));
                }
                catch (Exception ex)
                {
                    Client.UndoCheckOut(component.Id, true, new ReadOptions());

                    result.Status = Status.Error;
                    result.StackTrace = ex.StackTrace;
                    result.Message = String.Format("Error for \"{0}\"", component.GetWebDav().CutPath("/", 90, true));
                }
            }
            else
            {
                Client.UndoCheckOut(component.Id, true, new ReadOptions());

                result.Status = Status.Error;
                result.Message = String.Format("Error for \"{0}\"", component.GetWebDav().CutPath("/", 90, true));
            }

            results.Add(result);
        }

        public static void ChangeSchemasForComponentsInFolder(string host, string username, string password, string folderUri, bool recursive, string sourceSchemaUri, string targetSchemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            ChangeSchemasForComponentsInFolder(folderUri, recursive, sourceSchemaUri, targetSchemaUri, fieldMapping, results);
        }

        private static void ChangeSchemasForComponentsInFolder(string folderUri, bool recursive, string sourceSchemaUri, string targetSchemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            // Open up the source schema 
            SchemaData sourceSchema = Client.Read(sourceSchemaUri, null) as SchemaData;
            if (sourceSchema == null)
                return;

            // Open up the schema that we will be changing to
            SchemaData targetSchema = Client.Read(targetSchemaUri, null) as SchemaData;
            if (targetSchema == null)
                return;

            List<ItemFieldDefinitionData> targetSchemaFields = GetSchemaFields(targetSchemaUri);
            List<ItemFieldDefinitionData> targetSchemaMetadataFields = GetSchemaMetadataFields(targetSchemaUri);

            // Change container folder linked schema
            ChangeFolderLinkedSchema(folderUri, sourceSchemaUri, targetSchemaUri, results);

            // Change inner folder linked schemas
            foreach (ItemInfo item in GetFolders(folderUri, recursive))
            {
                ChangeFolderLinkedSchema(item.TcmId, sourceSchemaUri, targetSchemaUri, results);
            }

            // Change schema for components
            foreach (ItemInfo item in GetComponents(folderUri, recursive))
            {
                ChangeSchemaForComponent(item.TcmId, sourceSchema, targetSchema, targetSchemaFields, targetSchemaMetadataFields, fieldMapping, results);
            }
        }

        private static void ChangeFolderLinkedSchema(string folderUri, string sourceSchemaUri, string targetSchemaUri, List<ResultInfo> results)
        {
            FolderData innerFolder = Client.Read(folderUri, new ReadOptions()) as FolderData;

            if (innerFolder == null)
                return;

            if (innerFolder.LinkedSchema == null)
                return;

            if (!innerFolder.LinkedSchema.IdRef.Equals(sourceSchemaUri))
            {
                // If the component is not of the schmea that we want to change from, do nothing...
                return;
            }

            if (innerFolder.LinkedSchema.IdRef.Equals(targetSchemaUri))
            {
                // If the component already has this schema, don't do anything.
                return;
            }

            ResultInfo result = new ResultInfo();
            result.ItemType = ItemType.Folder;
            result.TcmId = folderUri;

            if (innerFolder.IsEditable.Value)
            {
                try
                {
                    //change schema id
                    innerFolder.LinkedSchema.IdRef = targetSchemaUri;

                    //make non-mandatory to aviod conflicts with inner components
                    innerFolder.IsLinkedSchemaMandatory = false;

                    Client.Save(innerFolder, new ReadOptions());

                    result.Status = Status.Success;
                    result.Message = String.Format("Changed schema for folder \"{0}\"", innerFolder.GetWebDav().CutPath("/", 80, true));
                }
                catch (Exception ex)
                {
                    result.Status = Status.Error;
                    result.StackTrace = ex.StackTrace;
                    result.Message = String.Format("Error for folder \"{0}\"", innerFolder.GetWebDav().CutPath("/", 80, true));
                }
            }
            else
            {
                result.Status = Status.Error;
                result.Message = String.Format("Error for folder \"{0}\"", innerFolder.GetWebDav().CutPath("/", 80, true));
            }

            results.Add(result);
        }

        private static void RemoveFolderLinkedSchema(string folderUri)
        {
            FolderData innerFolder = Client.Read(folderUri, new ReadOptions()) as FolderData;

            if (innerFolder == null)
                return;

            if (innerFolder.LinkedSchema == null)
                return;

            if (innerFolder.IsEditable.Value)
            {
                try
                {
                    //change schema id
                    innerFolder.LinkedSchema.IdRef = "tcm:0-0-0";

                    //make non-mandatory to aviod conflicts with inner components
                    innerFolder.IsLinkedSchemaMandatory = false;

                    Client.Save(innerFolder, new ReadOptions());
                }
                catch (Exception)
                {

                }
            }
        }

        private static void RemoveMetadataSchema(string itemUri)
        {
            RepositoryLocalObjectData item = Client.Read(itemUri, new ReadOptions()) as RepositoryLocalObjectData;

            if (item == null)
                return;

            if (item.MetadataSchema == null)
                return;

            if (item.IsEditable.Value)
            {
                try
                {
                    //change schema id
                    item.MetadataSchema.IdRef = "tcm:0-0-0";

                    Client.Save(item, new ReadOptions());
                }
                catch (Exception)
                {

                }
            }
        }

        public static void FixComponent(string host, string username, string password, string componentUri, string schemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            // Open up the schema
            SchemaData schema = Client.Read(schemaUri, null) as SchemaData;
            if (schema == null)
                return;

            List<ItemFieldDefinitionData> schemaFields = GetSchemaFields(schemaUri);
            List<ItemFieldDefinitionData> schemaMetadataFields = GetSchemaMetadataFields(schemaUri);

            // Fix component
            FixComponent(componentUri, schema, schemaFields, schemaMetadataFields, fieldMapping, results);
        }

        private static void FixComponent(string componentUri, SchemaData schema, List<ItemFieldDefinitionData> schemaFields, List<ItemFieldDefinitionData> schemaMetadataFields, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            if (string.IsNullOrEmpty(componentUri))
                return;
            
            ComponentData component = Client.Read(componentUri, new ReadOptions()) as ComponentData;
            if (component == null)
                return;

            if (!component.Schema.IdRef.Equals(schema.Id))
            {
                // If the component is not of the schema, do nothing...
                return;
            }

            //get fixed xml
            string newContent = schemaFields.Count == 0 ? string.Empty : GetComponentFixedContent(schema, schema.RootElementName, schemaFields, component.Content, schema.NamespaceUri, componentUri, fieldMapping, results);
            string newMetadata = schemaMetadataFields.Count == 0 ? string.Empty : GetComponentFixedContent(schema, "Metadata", schemaMetadataFields, component.Metadata, schema.NamespaceUri, componentUri, fieldMapping, results);

            if (component.Content.PrettyXml() == newContent.PrettyXml() && component.Metadata.PrettyXml() == newMetadata.PrettyXml())
                return;

            if (string.IsNullOrEmpty(newContent))
                return;

            ResultInfo result = new ResultInfo();
            result.ItemType = ItemType.Component;
            result.TcmId = component.Id;

            component = Client.TryCheckOut(component.Id, new ReadOptions()) as ComponentData;

            if (component.IsEditable.Value)
            {
                try
                {
                    //rebild component xml
                    component.Content = newContent;

                    //rebuild metadata
                    component.Metadata = newMetadata;

                    Client.Save(component, new ReadOptions());
                    Client.CheckIn(component.Id, new ReadOptions());

                    result.Status = Status.Success;
                    result.Message = String.Format("Fixed \"{0}\"", component.GetWebDav().CutPath("/", 90, true));
                }
                catch (Exception ex)
                {
                    Client.UndoCheckOut(component.Id, true, new ReadOptions());

                    result.Status = Status.Error;
                    result.StackTrace = ex.StackTrace;
                    result.Message = String.Format("Error for \"{0}\"", component.GetWebDav().CutPath("/", 90, true));
                }
            }
            else
            {
                Client.UndoCheckOut(component.Id, true, new ReadOptions());
                result.Status = Status.Error;
                result.Message = String.Format("Error for \"{0}\"", component.GetWebDav().CutPath("/", 90, true));
            }

            results.Add(result);
        }

        public static void FixComponents(string host, string username, string password, string folderUri, bool recursive, string schemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            FixComponents(folderUri, recursive, schemaUri, fieldMapping, results);
        }

        private static void FixComponents(string folderUri, bool recursive, string schemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            // Open up the schema that we will be changing
            SchemaData schema = Client.Read(schemaUri, null) as SchemaData;
            if (schema == null)
                return;

            List<ItemFieldDefinitionData> schemaFields = GetSchemaFields(schemaUri);
            List<ItemFieldDefinitionData> schemaMetadataFields = GetSchemaMetadataFields(schemaUri);

            // Fix components
            foreach (ItemInfo item in GetComponents(folderUri, recursive))
            {
                FixComponent(item.TcmId, schema, schemaFields, schemaMetadataFields, fieldMapping, results);
            }
        }

        public static void TransformComponent(string host, string username, string password, string componentUri, string targetFolderUri, string sourceSchemaUri, string targetSchemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            if (string.IsNullOrEmpty(componentUri))
                return;

            if (!EnsureValidClient(host, username, password))
                return;

            // Open up the source component schema 
            SchemaData sourceSchema = Client.Read(sourceSchemaUri, null) as SchemaData;
            if (sourceSchema == null)
                return;

            // Open up the target component schema
            SchemaData targetSchema = Client.Read(targetSchemaUri, null) as SchemaData;
            if (targetSchema == null)
                return;

            List<ItemFieldDefinitionData> targetSchemaFields = GetSchemaFields(targetSchemaUri);
            List<ItemFieldDefinitionData> targetSchemaMetadataFields = GetSchemaMetadataFields(targetSchemaUri);

            // Change schema for component
            TransformComponent(componentUri, targetFolderUri, sourceSchema, targetSchema, targetSchemaFields, targetSchemaMetadataFields, fieldMapping, results);
        }

        private static void TransformComponent(string componentUri, string targetFolderUri, SchemaData sourceSchema, SchemaData targetSchema, List<ItemFieldDefinitionData> targetSchemaFields, List<ItemFieldDefinitionData> targetSchemaMetadataFields, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            ComponentData component = Client.Read(componentUri, new ReadOptions()) as ComponentData;
            if (component == null || string.IsNullOrEmpty(component.Content))
                return;

            //get fixed xml
            string newContent = targetSchemaFields.Count == 0 ? string.Empty : GetComponentFixedContent(targetSchema, targetSchema.RootElementName, targetSchemaFields, component.Content, sourceSchema.NamespaceUri, componentUri, fieldMapping, results);
            string newMetadata = targetSchemaMetadataFields.Count == 0 ? string.Empty : GetComponentFixedContent(targetSchema, "Metadata", targetSchemaMetadataFields, component.Metadata, sourceSchema.NamespaceUri, componentUri, fieldMapping, results);

            if (string.IsNullOrEmpty(newContent))
                return;

            ResultInfo result = SaveComponent(targetSchema, component.Title, newContent, newMetadata, targetFolderUri);

            results.Add(result);
        }

        public static void TransformComponentsFromFolder(string host, string username, string password, string sourceFolderUri, bool recursive, string targetFolderUri, string sourceSchemaUri, string targetSchemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            TransformComponentsFromFolder(sourceFolderUri, recursive, targetFolderUri, sourceSchemaUri, targetSchemaUri, fieldMapping, results);
        }

        private static void TransformComponentsFromFolder(string sourceFolderUri, bool recursive, string targetFolderUri, string sourceSchemaUri, string targetSchemaUri, List<FieldMappingInfo> fieldMapping, List<ResultInfo> results)
        {
            // Open up the source component schema 
            SchemaData sourceSchema = Client.Read(sourceSchemaUri, null) as SchemaData;
            if (sourceSchema == null)
                return;

            // Open up the target component schema
            SchemaData targetSchema = Client.Read(targetSchemaUri, null) as SchemaData;
            if (targetSchema == null)
                return;

            List<ItemFieldDefinitionData> targetSchemaFields = GetSchemaFields(targetSchemaUri);
            List<ItemFieldDefinitionData> targetSchemaMetadataFields = GetSchemaMetadataFields(targetSchemaUri);

            // Transform components
            foreach (ItemInfo item in GetComponents(sourceFolderUri, recursive))
            {
                TransformComponent(item.TcmId, targetFolderUri, sourceSchema, targetSchema, targetSchemaFields, targetSchemaMetadataFields, fieldMapping, results);
            }
        }

        #endregion

        #region Tridion pages

        public static List<ItemInfo> GetPages(string host, string username, string password, string tcmComponent)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return GetPages(tcmComponent);
        }

        private static List<ItemInfo> GetPages(string tcmComponent)
        {
            return Client.GetListXml(tcmComponent, new UsingItemsFilterData { ItemTypes = new[] { ItemType.Page } }).ToList(ItemType.Page);
        }

        #endregion

        #region Broken data

        public static List<ResultInfo> CheckBrokenComponentsAndMetadata(string host, string username, string password, string tcmItem)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            List<ResultInfo> results = new List<ResultInfo>();

            ItemType itemType = GetItemType(tcmItem);
            if (itemType == ItemType.Publication)
            {
                List<ItemInfo> schemas = GetSchemas(tcmItem);
                CheckBrokenComponentsAndMetadata(schemas, results);
            }
            else if (itemType == ItemType.Folder)
            {
                List<ItemInfo> schemas = GetSchemas(tcmItem, false);
                CheckBrokenComponentsAndMetadata(schemas, results);
            }
            else
            {
                List<ItemInfo> schemas = new List<ItemInfo> { new ItemInfo { TcmId =  tcmItem }};
                CheckBrokenComponentsAndMetadata(schemas, results);
            }

            results = results.Distinct(new ResultInfoComparer()).ToList();

            ResultInfo resultFinish = new ResultInfo();
            resultFinish.Status = Status.Info;
            resultFinish.Message = "Finished";
            results.Add(resultFinish);

            return results;
        }

        private static void CheckBrokenComponentsAndMetadata(List<ItemInfo> schemaItems, List<ResultInfo> results)
        {
            foreach (ItemInfo schemaItem in schemaItems)
            {
                SchemaData schema = ReadItem(schemaItem.TcmId) as SchemaData;
                if (schema == null)
                    continue;

                List<ItemFieldDefinitionData> schemaFields = GetSchemaFields(schemaItem.TcmId);
                List<ItemFieldDefinitionData> schemaMetadataFields = GetSchemaMetadataFields(schemaItem.TcmId);

                if (schema.Purpose == SchemaPurpose.Component)
                    CheckBrokenComponents(schema, schemaFields, results);

                if (schema.Purpose == SchemaPurpose.Metadata || schema.Purpose == SchemaPurpose.Multimedia)
                    CheckBrokenMetadata(schema, schemaMetadataFields, results);
            }
        }

        private static void CheckBrokenComponents(SchemaData schema, List<ItemFieldDefinitionData> schemaFields, List<ResultInfo> results)
        {
            List<string> components = GetUsingItems(schema.Id, true, new[] { ItemType.Component });

            foreach (string componentUri in components)
            {
                ComponentData component = GetComponent(componentUri);

                CheckBrokenContent(schema, schema.RootElementName, schemaFields, component.Content, schema.NamespaceUri, component, results);
            }
        }

        private static void CheckBrokenMetadata(SchemaData schema, List<ItemFieldDefinitionData> schemaFields, List<ResultInfo> results)
        {
            List<string> metadataObjects = GetUsingCurrentItems(schema.Id);
            foreach (string itemUri in metadataObjects)
            {
                IdentifiableObjectData item = ReadItem(itemUri);

                if (item is RepositoryLocalObjectData || item is PublicationData)
                {
                    string metadata = null;
                    if (item is RepositoryLocalObjectData)
                        metadata = ((RepositoryLocalObjectData)item).Metadata;
                    if (item is PublicationData)
                        metadata = ((PublicationData)item).Metadata;

                    if (!String.IsNullOrEmpty(metadata))
                        CheckBrokenContent(schema, "Metadata", schemaFields, metadata, schema.NamespaceUri, item, results);
                }
            }
        }

        public static void CheckBrokenContent(SchemaData schema, string rootElementName, List<ItemFieldDefinitionData> schemaFields, string xml, XNamespace ns, IdentifiableObjectData item, List<ResultInfo> results)
        {
            List<FieldMappingInfo> fieldMapping = GetDefaultFieldMapping(schemaFields, schema.Id);

            List<ComponentFieldData> componentFieldDataValues = GetComponentValues(schema, rootElementName, schemaFields, xml, ns, item.Id, fieldMapping, results);

            //check mandatory and empty items
            foreach (ItemFieldDefinitionData schemaField in schemaFields)
            {
                if (schemaField.IsMandatory())
                {
                    ComponentFieldData componentFieldDataValue = componentFieldDataValues.FirstOrDefault(x => x.SchemaField.Name == schemaField.Name);

                    if (!schemaField.IsMultiValue() && (componentFieldDataValue == null || componentFieldDataValue.Value == null) || 
                        schemaField.IsMultiValue() && (componentFieldDataValue == null || componentFieldDataValue.Value == null || !((IEnumerable<object>)componentFieldDataValue.Value).Any()))
                    {
                        ResultInfo result = new ResultInfo();
                        result.ItemType = GetItemType(item.Id);
                        result.TcmId = item.Id;
                        result.Status = Status.Error;
                        result.Message = String.Format("Element \"{0}\" contains mandatory empty fields", item is RepositoryLocalObjectData ? ((RepositoryLocalObjectData)item).GetWebDav().CutPath("/", 90, true) : item.Title);
                        results.Add(result);
                    }
                }
                else if (schemaField.IsEmbedded())
                {
                    ComponentFieldData componentFieldDataValue = componentFieldDataValues.FirstOrDefault(x => x.SchemaField.Name == schemaField.Name);
                    
                    if (componentFieldDataValue != null)
                    {
                        EmbeddedSchemaFieldDefinitionData embeddedSchemaField = (EmbeddedSchemaFieldDefinitionData)schemaField;
                        SchemaData embeddedSchema = ReadItem(embeddedSchemaField.EmbeddedSchema.IdRef) as SchemaData;
                        List<ItemFieldDefinitionData> embeddedSchemaFields = GetSchemaFields(embeddedSchemaField.EmbeddedSchema.IdRef);

                        if (!schemaField.IsMultiValue())
                        {
                            string value = componentFieldDataValue.Value.ToString();
                            if (embeddedSchema != null)
                            {
                                CheckBrokenContent(embeddedSchema, schemaField.Name, embeddedSchemaFields, value, ns, item, results);
                            }
                        }
                        else
                        {
                            foreach (XElement element in (IEnumerable<object>)componentFieldDataValue.Value)
                            {
                                string value = element.ToString();
                                if (embeddedSchema != null)
                                {
                                    CheckBrokenContent(embeddedSchema, schemaField.Name, embeddedSchemaFields, value, ns, item, results);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        #endregion

        #region Tridion delete

        public static void Delete(string host, string username, string password, string tcmItem, bool delete, List<ResultInfo> results)
        {
            ItemType itemType = GetItemType(tcmItem);
            if (itemType == ItemType.Publication)
            {
                DeletePublication(host, username, password, tcmItem, delete, results);
            }
            else if (itemType == ItemType.Folder || itemType == ItemType.StructureGroup)
            {
                DeleteFolderOrStructureGroup(host, username, password, tcmItem, delete, results);
            }
            else
            {
                DeleteTridionObject(host, username, password, tcmItem, delete, results);
            }
        }

        public static void DeleteTridionObject(string host, string username, string password, string tcmItem, bool delete, List<ResultInfo> results)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            DeleteTridionObject(tcmItem, string.Empty, delete, true, results, 0);
        }

        private static void DeleteTridionObject(string tcmItem, string parentTcmId, bool delete, bool currentVersion, List<ResultInfo> results, int level)
        {
            if (tcmItem.StartsWith("tcm:0-"))
                return;

            tcmItem = GetBluePrintTopTcmId(tcmItem);
            
            ItemType itemType = GetItemType(tcmItem);

            if (level > 3)
            {
                results.Add(new ResultInfo
                {
                    Message = "Recoursion level is too big. Try to select different item",
                    TcmId = tcmItem,
                    ItemType = itemType,
                    Status = Status.Error
                });

                return;
            }

            RepositoryLocalObjectData item = (RepositoryLocalObjectData)Client.Read(tcmItem, new ReadOptions());

            bool isAnyLocalized = IsAnyLocalized(tcmItem);

            List<string> usingItems = GetUsingItems(tcmItem);
            List<string> usingCurrentItems = GetUsingCurrentItems(tcmItem);

            if (currentVersion)
            {
                foreach (string usingItem in usingItems)
                {
                    DeleteTridionObject(usingItem, tcmItem, delete, usingCurrentItems.Any(x => x == usingItem), results, level + 1);
                }
            }

            //remove folder linked schema
            if (itemType == ItemType.Folder)
            {
                try
                {
                    FolderData folder = item as FolderData;
                    if (folder != null && folder.LinkedSchema != null && folder.LinkedSchema.IdRef != "tcm:0-0-0")
                    {
                        if (delete)
                            RemoveFolderLinkedSchema(tcmItem);

                        if (delete)
                        {
                            results.Add(new ResultInfo
                            {
                                Message = string.Format("Removed folder linked schema for \"{0}\"", item.GetWebDav().CutPath("/", 80, true)),
                                TcmId = tcmItem,
                                ItemType = itemType,
                                Status = Status.Success
                            });
                        }

                        else
                        {
                            results.Add(new ResultInfo
                            {
                                Message = string.Format("Remove folder linked schema for \"{0}\"", item.GetWebDav().CutPath("/", 80, true)),
                                TcmId = tcmItem,
                                ItemType = itemType,
                                Status = Status.Info
                            });
                        }
                    }
                }
                catch (Exception ex)
                {
                    results.Add(new ResultInfo
                    {
                        Message = string.Format("Error removing folder linked schema for \"{0}\"", item.GetWebDav().CutPath("/", 80, true)),
                        TcmId = tcmItem,
                        ItemType = itemType,
                        Status = Status.Error,
                        StackTrace = ex.StackTrace
                    });
                }
            }

            //remove metadata
            if (itemType == ItemType.Folder || itemType == ItemType.StructureGroup || itemType == ItemType.Publication)
            {
                try
                {
                    if (item != null && item.MetadataSchema != null && item.MetadataSchema.IdRef != "tcm:0-0-0")
                    {
                        if (delete)
                            RemoveMetadataSchema(tcmItem);

                        if (delete)
                        {
                            results.Add(new ResultInfo
                            {
                                Message = string.Format("Removed metadata schema for \"{0}\"", item.GetWebDav().CutPath("/", 80, true)),
                                TcmId = tcmItem,
                                ItemType = itemType,
                                Status = Status.Success
                            });
                        }

                        else
                        {
                            results.Add(new ResultInfo
                            {
                                Message = string.Format("Remove metadata schema for \"{0}\"", item.GetWebDav().CutPath("/", 80, true)),
                                TcmId = tcmItem,
                                ItemType = itemType,
                                Status = Status.Info
                            });
                        }
                    }
                }
                catch (Exception ex)
                {
                    results.Add(new ResultInfo
                    {
                        Message = string.Format("Error removing metadata schema for \"{0}\"", item.GetWebDav().CutPath("/", 80, true)),
                        TcmId = tcmItem,
                        ItemType = itemType,
                        Status = Status.Error,
                        StackTrace = ex.StackTrace
                    });
                }
            }

            try
            {
                if (delete)
                {
                    if (!currentVersion)
                    {
                        //remove used versions
                        List<XElement> history = GetItemHistory(tcmItem);
                        foreach (XElement xElement in history)
                        {
                            string historyItemTcmId = xElement.Attribute("ID").Value;
                            List<string> historyItemUsedItems = GetUsedItems(historyItemTcmId);
                            bool historyItemIsUsed = historyItemUsedItems.Any(x => x.Split('-')[1] == parentTcmId.Split('-')[1]);
                            if (historyItemIsUsed)
                            {
                                Client.Delete(historyItemTcmId);
                            }
                        }
                    }
                    else
                    {
                        //unlocalize before delete
                        if (isAnyLocalized)
                        {
                            UnLocalizeAll(tcmItem);
                        }

                        //undo checkout
                        try
                        {
                            Client.UndoCheckOut(tcmItem, true, new ReadOptions());
                        }
                        catch (Exception)
                        {
                        }

                        //delete used item
                        Client.Delete(tcmItem);
                    }
                }

                if (delete)
                {
                    results.Add(new ResultInfo
                    {
                        Message = string.Format("Deleted item \"{0}\"", item.GetWebDav().CutPath("/", 90, true)),
                        TcmId = tcmItem,
                        ItemType = itemType,
                        Status = Status.Success
                    });
                }
                else
                {
                    if (isAnyLocalized)
                    {
                        results.Add(new ResultInfo
                        {
                            Message = string.Format("Unlocalize item \"{0}\"", item.GetWebDav().CutPath("/", 90, true)),
                            TcmId = tcmItem,
                            ItemType = itemType,
                            Status = Status.Info
                        });
                    }

                    if (IsPublished(tcmItem))
                    {
                        results.Add(new ResultInfo
                        {
                            Message = string.Format("Unpublish manually item \"{0}\" published at {1}", item.GetWebDav().CutPath("/", 80, true), GetPublishInfo(tcmItem)),
                            TcmId = GetFirstPublishItemTcmId(tcmItem),
                            ItemType = itemType,
                            Status = Status.Warning
                        });
                    }

                    if (!currentVersion)
                    {
                        results.Add(new ResultInfo
                        {
                            Message = string.Format("Remove old versions of item \"{0}\"", item.GetWebDav().CutPath("/", 80, true)),
                            TcmId = tcmItem,
                            ItemType = itemType,
                            Status = Status.Info
                        });
                    }
                    else
                        results.Add(new ResultInfo
                        {
                            Message = string.Format("Delete item \"{0}\"", item.GetWebDav().CutPath("/", 90, true)),
                            TcmId = tcmItem,
                            ItemType = itemType,
                            Status = Status.Info
                        });
                }
            }
            catch (Exception ex)
            {
                results.Add(new ResultInfo
                {
                    Message = string.Format("Error deleting item \"{0}\"", item.GetWebDav().CutPath("/", 90, true)),
                    TcmId = tcmItem,
                    ItemType = itemType,
                    Status = Status.Error,
                    StackTrace = ex.StackTrace
                });
            }
        }

        public static void DeleteFolderOrStructureGroup(string host, string username, string password, string tcmFolder, bool delete, List<ResultInfo> results)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            DeleteFolderOrStructureGroup(tcmFolder, delete, results, 0, false);
        }

        private static void DeleteFolderOrStructureGroup(string tcmFolder, bool delete, List<ResultInfo> results, int level, bool onlyCurrentBluePrint)
        {
            bool isCurrentBluePrint = tcmFolder == GetBluePrintTopTcmId(tcmFolder);
            
            tcmFolder = GetBluePrintBottomTcmId(tcmFolder);

            List<ResultInfo> folderResults = new List<ResultInfo>();

            List<ItemInfo> childItems = GetItemsByParentContainer(tcmFolder);
            if (onlyCurrentBluePrint)
            {
                childItems = childItems.Where(x => string.IsNullOrEmpty(x.FromPub)).ToList();
            }

            //delete inner items
            foreach (ItemInfo childItem in childItems)
            {
                if (childItem.ItemType == ItemType.Folder || childItem.ItemType == ItemType.StructureGroup)
                {
                    DeleteFolderOrStructureGroup(childItem.TcmId, delete, folderResults, level, onlyCurrentBluePrint);
                }
                else
                {
                    DeleteTridionObject(childItem.TcmId, tcmFolder, delete, true, folderResults, level);
                }
            }

            results.AddRange(folderResults.Distinct(new ResultInfoComparer()));

            //delete folder or SG as an object
            if (!onlyCurrentBluePrint || isCurrentBluePrint)
                DeleteTridionObject(tcmFolder, string.Empty, delete, true, results, level);
        }

        public static void DeletePublication(string host, string username, string password, string tcmPublication, bool delete, List<ResultInfo> results)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            DeletePublication(tcmPublication, delete, results, 0);
        }

        private static void DeletePublication(string tcmPublication, bool delete, List<ResultInfo> results, int level)
        {
            PublicationData publication = (PublicationData)Client.Read(tcmPublication, new ReadOptions());

            //delete dependent publications
            List<string> usingItems = GetUsingItems(tcmPublication);
            foreach (string usingItem in usingItems)
            {
                ItemType itemType = GetItemType(usingItem);
                if (itemType == ItemType.Publication)
                {
                    DeletePublication(usingItem, delete, results, level + 1);
                }
                else
                {
                    DeleteTridionObject(usingItem, string.Empty, delete, true, results, level + 1);
                }
            }

            List<ResultInfo> publicationResults = new List<ResultInfo>();

            //delete / inform published items
            List<ItemInfo> pulishedItems = GetItemsByPublication(tcmPublication, true).Where(x => x.IsPublished).ToList();
            foreach (ItemInfo publishedItem in pulishedItems)
            {
                DeleteTridionObject(publishedItem.TcmId, string.Empty, delete, true, publicationResults, level);
            }

            //delete inner items
            List<ItemInfo> childItems = GetFoldersAndStructureGroupsByPublication(tcmPublication).Where(x => string.IsNullOrEmpty(x.FromPub)).ToList();
            foreach (ItemInfo childItem in childItems)
            {
                DeleteFolderOrStructureGroup(childItem.TcmId, delete, publicationResults, level, true);
            }

            results.AddRange(publicationResults.Distinct(new ResultInfoComparer()));

            try
            {
                //delete publication as an object
                if (delete)
                {
                    Client.Delete(tcmPublication);
                }

                if (delete)
                {
                    results.Add(new ResultInfo
                    {
                        Message = string.Format("Deleted publication \"{0}\"", publication.Title),
                        TcmId = tcmPublication,
                        ItemType = ItemType.Publication,
                        Status = Status.Success
                    });
                }
                else
                {
                    results.Add(new ResultInfo
                    {
                        Message = string.Format("Delete publication \"{0}\"", publication.Title),
                        TcmId = tcmPublication,
                        ItemType = ItemType.Publication,
                        Status = Status.Info
                    });
                }
            }
            catch (Exception ex)
            {
                results.Add(new ResultInfo
                {
                    Message = string.Format("Error deleting publication \"{0}\"", publication.Title),
                    TcmId = tcmPublication,
                    ItemType = ItemType.Publication,
                    Status = Status.Error,
                    StackTrace = ex.StackTrace
                });
            }
        }

        #endregion

        #region Tridion schema relocation

        public static void SetItemDependencyList(string itemId, List<string> preItems)
        {
            if (preItems.Any(x => x == itemId))
                return;

            preItems.Add(itemId);

            List<string> usedItems = GetUsedItems(itemId);
            if (usedItems == null || usedItems.Count == 0)
                return;

            foreach (string usedItemId in usedItems)
            {
                SetItemDependencyList(usedItemId, preItems);
            }
        }

        private static string ChangeSchemaLocation(string id, string targetFolderId, Dictionary<string, string> mapping)
        {
            SchemaData sourceSchema = ReadItem(id) as SchemaData;
            if (sourceSchema == null)
                return "";

            List<ItemInfo> targetFolderItems = GetItemsByParentContainer(targetFolderId);
            if (targetFolderItems.Any(x => x.Title == sourceSchema.Title))
                return targetFolderItems.First(x => x.Title == sourceSchema.Title).TcmId;

            string xsd = sourceSchema.Xsd;

            foreach (KeyValuePair<string, string> pair in mapping)
            {
                if (xsd.Contains(pair.Key) && pair.Value != "")
                    xsd = xsd.Replace(pair.Key, pair.Value);
            }

            xsd = xsd.Replace("tcm:2102-", "tcm:2101-");

            SchemaData targetSchema = new SchemaData
            {
                Title = sourceSchema.Title,
                Description = sourceSchema.Description,
                RootElementName = sourceSchema.RootElementName,
                Purpose = sourceSchema.Purpose,
                LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = targetFolderId } },
                Id = "tcm:0-0-0",
                Xsd = xsd,
                AllowedMultimediaTypes = sourceSchema.AllowedMultimediaTypes,
            };

            targetSchema = Client.Save(targetSchema, new ReadOptions()) as SchemaData;

            if (targetSchema == null)
                return "";

            return Client.CheckIn(targetSchema.Id, new ReadOptions()).Id;
        }

        public static void ChangeSchemaChainLocation(string host, string username, string password, string schemaId, string sourceFolderId, string targetFolderId)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            ChangeSchemaChainLocation(schemaId, sourceFolderId, targetFolderId);
        }

        private static void ChangeSchemaChainLocation(string schemaId, string sourceFolderId, string targetFolderId)
        {
            List<string> items = new List<string>();

            SetItemDependencyList(schemaId, items);

            items.Reverse();

            Dictionary<string, string> mapping = items.ToDictionary(itemId => itemId, itemId => "");

            foreach (string itemId in items)
            {
                string newId = ChangeSchemaLocation(itemId, targetFolderId, mapping);

                if (newId == "")
                    continue;

                mapping[itemId] = newId;
            }
        }

        public static void ChangeFolderSchemaChainLocation(string host, string username, string password, string sourceFolderId, string targetFolderId)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            ChangeFolderSchemaChainLocation(sourceFolderId, targetFolderId);
        }

        private static void ChangeFolderSchemaChainLocation(string sourceFolderId, string targetFolderId)
        {
            List<ItemInfo> sourceSchemas = GetItemsByParentContainer(sourceFolderId, true);
            foreach (ItemInfo schema in sourceSchemas)
            {
                ChangeSchemaChainLocation(schema.TcmId, sourceFolderId, targetFolderId);
            }
        }

        #endregion

        #region Tridion publishing

        private static bool IsPublished(string tcmItem)
        {
            return Client.GetListPublishInfo(tcmItem).Any();
        }

        private static string GetPublishInfo(string tcmItem)
        {
            return string.Join(", ", Client.GetListPublishInfo(tcmItem).Select(p => string.Format("\"{0}\"", Client.Read(p.Repository.IdRef, new ReadOptions()).Title)).ToArray());
        }

        private static string GetFirstPublishItemTcmId(string tcmItem)
        {
            return GetBluePrintItemTcmId(tcmItem, Client.GetListPublishInfo(tcmItem).First().Repository.IdRef);
        }

        #endregion

        #region Tridion Blueprint

        public static string GetBluePrintTopTcmId(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return String.Empty;

            return GetBluePrintTopTcmId(id);
        }

        private static string GetBluePrintTopTcmId(string id)
        {
            if (id.StartsWith("tcm:0-"))
                return id;

            var list = Client.GetSystemWideList(new BluePrintFilterData { ForItem = new LinkToRepositoryLocalObjectData { IdRef = id } });
            if (list == null || list.Length == 0)
                return id;

            var list2 = list.Cast<BluePrintNodeData>().Where(x => x.Item != null).ToList();

            return list2.First().Item.Id;
        }

        public static string GetBluePrintBottomTcmId(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return String.Empty;

            return GetBluePrintBottomTcmId(id);
        }

        private static string GetBluePrintBottomTcmId(string id)
        {
            if (id.StartsWith("tcm:0-"))
                return id;

            var list = Client.GetSystemWideList(new BluePrintFilterData { ForItem = new LinkToRepositoryLocalObjectData { IdRef = id } });
            if (list == null || list.Length == 0)
                return id;

            var list2 = list.Cast<BluePrintNodeData>().Where(x => x.Item != null).ToList();

            return list2.Last().Item.Id;
        }

        public static string GetBluePrintTopLocalizedTcmId(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return String.Empty;

            return GetBluePrintTopLocalizedTcmId(id);
        }

        private static string GetBluePrintTopLocalizedTcmId(string id)
        {
            if (id.StartsWith("tcm:0-"))
                return id;

            var list = Client.GetSystemWideList(new BluePrintFilterData { ForItem = new LinkToRepositoryLocalObjectData { IdRef = id } });
            if (list == null || list.Length == 0)
                return id;

            var item = list.Cast<BluePrintNodeData>().FirstOrDefault(x => x.Item != null && x.Item.Id == id);
            if (item == null)
                return id;

            string publicationId = item.Item.BluePrintInfo.OwningRepository.IdRef;

            return GetBluePrintItemTcmId(id, publicationId);
        }

        public static bool IsLocalized(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            return IsLocalized(id);
        }

        private static bool IsLocalized(string id)
        {
            return id == GetBluePrintTopLocalizedTcmId(id) && id != GetBluePrintTopTcmId(id);
        }

        public static bool IsAnyLocalized(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            return IsAnyLocalized(id);
        }

        private static bool IsAnyLocalized(string id)
        {
            var list = Client.GetSystemWideList(new BluePrintFilterData { ForItem = new LinkToRepositoryLocalObjectData { IdRef = id } });
            if (list == null || list.Length == 0)
                return false;

            return list.Cast<BluePrintNodeData>().Any(x => x.Item != null && IsLocalized(x.Item.Id));
        }

        public static bool IsShared(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return false;

            return IsShared(id);
        }

        private static bool IsShared(string id)
        {
            return id != GetBluePrintTopTcmId(id) && id != GetBluePrintTopLocalizedTcmId(id);
        }

        public static void UnLocalize(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            UnLocalize(id);
        }

        private static void UnLocalize(string id)
        {
            if(IsLocalized(id))
                Client.UnLocalize(id, new ReadOptions());
        }

        public static void UnLocalizeAll(string host, string username, string password, string id)
        {
            if (!EnsureValidClient(host, username, password))
                return;

            UnLocalizeAll(id);
        }

        private static void UnLocalizeAll(string id)
        {
            if (!IsAnyLocalized(id))
                return;

            var list = Client.GetSystemWideList(new BluePrintFilterData { ForItem = new LinkToRepositoryLocalObjectData { IdRef = id } });
            if (list == null || list.Length == 0)
                return;

            var list2 = list.Cast<BluePrintNodeData>().Where(x => x.Item != null).ToList();

            foreach (BluePrintNodeData item in list2)
            {
                if(IsLocalized(item.Item.Id))
                    UnLocalize(item.Item.Id);
            }
        }

        #endregion

        #region Collection helpers

        public static List<ItemInfo> Intersect(List<ItemInfo>[] arrayOfSets, bool includeEmptySets)
        {
            List<ItemInfo>[] arr = includeEmptySets ? arrayOfSets : arrayOfSets.Where(x => x != null && x.Count > 0).ToArray();

            if (arr.Length == 0 || arr.Length == 1 && arr[0].Count == 0) return new List<ItemInfo>();
            if (arr.Length == 1 && arr[0].Count > 0) return arr[0];

            List<ItemInfo> res = arr[0];
            for (int i = 1; i < arr.Length; i++)
            {
                res = res.Intersect(arr[i], new ItemInfoComparer()).ToList();
            }

            return res;
        }

        private class ItemInfoComparer : IEqualityComparer<ItemInfo>
        {
            public bool Equals(ItemInfo x, ItemInfo y)
            {
                return x.TcmId == y.TcmId;
            }

            public int GetHashCode(ItemInfo obj)
            {
                return obj.TcmId.GetHashCode();
            }
        }

        private class ResultInfoComparer : IEqualityComparer<ResultInfo>
        {
            public bool Equals(ResultInfo x, ResultInfo y)
            {
                return x.TcmId == y.TcmId && x.Status == y.Status;
            }

            public int GetHashCode(ResultInfo obj)
            {
                return obj.TcmId.GetHashCode();
            }
        }

        public static List<ItemInfo> ToList(this XElement xml, ItemType itemType)
        {
            List<ItemInfo> res = new List<ItemInfo>();
            if (xml != null && xml.HasElements)
            {
                foreach (XElement element in xml.Nodes())
                {
                    ItemInfo item = new ItemInfo();
                    item.TcmId = element.Attribute("ID").Value;
                    item.ItemType = itemType;
                    item.Title = element.Attributes().Any(x => x.Name == "Title") ? element.Attribute("Title").Value : item.TcmId;
                    item.MimeType = element.Attributes().Any(x => x.Name == "MIMEType") ? element.Attribute("MIMEType").Value : null;
                    item.FromPub = element.Attributes().Any(x => x.Name == "FromPub") ? element.Attribute("FromPub").Value : null;
                    item.IsPublished = element.Attributes().Any(x => x.Name == "Icon") && element.Attribute("Icon").Value.EndsWith("P1");
                    res.Add(item);
                }
            }
            return res;
        }

        public static List<ItemInfo> ToList(this XElement xml)
        {
            List<ItemInfo> res = new List<ItemInfo>();
            if (xml != null && xml.HasElements)
            {
                foreach (XElement element in xml.Nodes())
                {
                    ItemInfo item = new ItemInfo();
                    item.TcmId = element.Attribute("ID").Value;
                    item.ItemType = element.Attributes().Any(x => x.Name == "Type") ? (ItemType)Int32.Parse(element.Attribute("Type").Value) : GetItemType(item.TcmId);
                    item.Title = element.Attributes().Any(x => x.Name == "Title") ? element.Attribute("Title").Value : item.TcmId;
                    item.MimeType = element.Attributes().Any(x => x.Name == "MIMEType") ? element.Attribute("MIMEType").Value : null;
                    item.FromPub = element.Attributes().Any(x => x.Name == "FromPub") ? element.Attribute("FromPub").Value : null;
                    item.IsPublished = element.Attributes().Any(x => x.Name == "Icon") && element.Attribute("Icon").Value.EndsWith("P1");
                    res.Add(item);
                }
            }
            return res;
        }

        public static ItemType GetItemType(string tcmItem)
        {
            string[] arr = tcmItem.Replace("tcm:", string.Empty).Split('-');
            if (arr.Length == 2) return ItemType.Component;

            return (ItemType)Int32.Parse(arr[2]);
        }

        public static List<ItemInfo> MakeExpandable(this List<ItemInfo> list)
        {
            foreach (ItemInfo item in list)
            {
                if (item.ChildItems == null && (item.ItemType == ItemType.Publication || item.ItemType == ItemType.Folder || item.ItemType == ItemType.StructureGroup))
                    item.ChildItems = new List<ItemInfo> { new ItemInfo { Title = "Loading..." } };
            }
            return list;
        }

        public static List<ItemInfo> Expand(this List<ItemInfo> list, string host, string username, string password, TridionSelectorMode tridionSelectorMode, List<string> tcmItemPath, string selectedTcmId)
        {
            if (tcmItemPath == null || String.IsNullOrEmpty(selectedTcmId))
                return list;

            foreach (ItemInfo item in list)
            {
                if (tcmItemPath.Any(x => x == item.TcmId))
                {
                    item.IsExpanded = true;
                    item.IsSelected = item.TcmId == selectedTcmId;

                    if (item.IsSelected)
                        continue;

                    if (String.IsNullOrEmpty(item.TcmId))
                        continue;

                    item.ChildItems = null;
                    if (item.ItemType == ItemType.Publication)
                    {
                        if (tridionSelectorMode == TridionSelectorMode.Folder || tridionSelectorMode == TridionSelectorMode.FolderAndComponent || tridionSelectorMode == TridionSelectorMode.FolderAndSchema)
                        {
                            item.ChildItems = GetFoldersByPublication(host, username, password, item.TcmId);
                        }
                        if (tridionSelectorMode == TridionSelectorMode.StructureGroup)
                        {
                            item.ChildItems = GetStructureGroupsByPublication(host, username, password, item.TcmId);
                        }
                        if (tridionSelectorMode == TridionSelectorMode.FolderAndStructureGroup || tridionSelectorMode == TridionSelectorMode.AnyObject)
                        {
                            item.ChildItems = GetFoldersAndStructureGroupsByPublication(host, username, password, item.TcmId);
                        }
                    }
                    else if (tridionSelectorMode == TridionSelectorMode.FolderAndComponent && item.ItemType == ItemType.Folder)
                    {
                        item.ChildItems = GetFoldersAndComponentsByParentFolder(host, username, password, item.TcmId);
                    }
                    else if (tridionSelectorMode == TridionSelectorMode.FolderAndSchema && item.ItemType == ItemType.Folder)
                    {
                        item.ChildItems = GetFoldersAndSchemasByParentFolder(host, username, password, item.TcmId);
                    }
                    else if (tridionSelectorMode == TridionSelectorMode.AnyObject && (item.ItemType == ItemType.Folder || item.ItemType == ItemType.StructureGroup))
                    {
                        item.ChildItems = GetItemsByParentContainer(host, username, password, item.TcmId);
                    }
                    else if (item.ItemType == ItemType.Folder)
                    {
                        item.ChildItems = GetFoldersByParentFolder(host, username, password, item.TcmId);
                    }
                    else if (item.ItemType == ItemType.StructureGroup)
                    {
                        item.ChildItems = GetStructureGroupsByParentStructureGroup(host, username, password, item.TcmId);
                    }

                    if (item.ChildItems != null && item.ChildItems.Count > 0)
                    {
                        item.ChildItems.SetParent(item);
                    }

                    if (item.ChildItems != null && item.ChildItems.Count > 0)
                    {
                        item.ChildItems.Expand(host, username, password, tridionSelectorMode, tcmItemPath, selectedTcmId);
                    }
                }
                else
                {
                    if (item.ItemType == ItemType.Publication || item.ItemType == ItemType.Folder || item.ItemType == ItemType.StructureGroup)
                        item.ChildItems = new List<ItemInfo> { new ItemInfo { Title = "Loading..." } };
                }
            }
            return list;
        }

        public static void OnItemExpanded(string host, string username, string password, ItemInfo item, TridionSelectorMode tridionSelectorMode)
        {
            if (item.ChildItems != null && item.ChildItems.All(x => x.Title != "Loading..."))
                return;

            if (String.IsNullOrEmpty(item.TcmId))
                return;

            if (item.ItemType == ItemType.Publication)
            {
                if (tridionSelectorMode == TridionSelectorMode.Folder || tridionSelectorMode == TridionSelectorMode.FolderAndComponent || tridionSelectorMode == TridionSelectorMode.FolderAndSchema)
                {
                    item.ChildItems = GetFoldersByPublication(host, username, password, item.TcmId).MakeExpandable().SetParent(item);
                }
                if (tridionSelectorMode == TridionSelectorMode.StructureGroup)
                {
                    item.ChildItems = GetStructureGroupsByPublication(host, username, password, item.TcmId).MakeExpandable().SetParent(item);
                }
                if (tridionSelectorMode == TridionSelectorMode.FolderAndStructureGroup || tridionSelectorMode == TridionSelectorMode.AnyObject)
                {
                    item.ChildItems = GetFoldersAndStructureGroupsByPublication(host, username, password, item.TcmId).MakeExpandable().SetParent(item);
                }
            }
            else if (tridionSelectorMode == TridionSelectorMode.FolderAndComponent && item.ItemType == ItemType.Folder)
            {
                item.ChildItems = GetFoldersAndComponentsByParentFolder(host, username, password, item.TcmId).MakeExpandable().SetParent(item);
            }
            else if (tridionSelectorMode == TridionSelectorMode.FolderAndSchema && item.ItemType == ItemType.Folder)
            {
                item.ChildItems = GetFoldersAndSchemasByParentFolder(host, username, password, item.TcmId).MakeExpandable().SetParent(item);
            }
            else if (tridionSelectorMode == TridionSelectorMode.AnyObject && (item.ItemType == ItemType.Folder || item.ItemType == ItemType.StructureGroup))
            {
                item.ChildItems = GetItemsByParentContainer(host, username, password, item.TcmId).MakeExpandable().SetParent(item);
            }
            else if (item.ItemType == ItemType.Folder)
            {
                item.ChildItems = GetFoldersByParentFolder(host, username, password, item.TcmId).MakeExpandable().SetParent(item);
            }
            else if (item.ItemType == ItemType.StructureGroup)
            {
                item.ChildItems = GetStructureGroupsByParentStructureGroup(host, username, password, item.TcmId).MakeExpandable().SetParent(item);
            }
        }

        public static void AddPathItem(List<ItemInfo> list, ItemInfo item)
        {
            if (item == null)
                return;

            list.Add(item);

            if (item.Parent != null)
                AddPathItem(list, item.Parent);
        }

        public static List<ItemInfo> SetParent(this List<ItemInfo> list, ItemInfo parent)
        {
            foreach (ItemInfo item in list)
            {
                item.Parent = parent;
            }
            return list;
        }

        public static string GetMimeTypeId(string host, string username, string password, string filePath)
        {
            if (!EnsureValidClient(host, username, password))
                return String.Empty;

            List<MultimediaTypeData> allMimeTypes = Client.GetSystemWideList(new MultimediaTypesFilterData()).Cast<MultimediaTypeData>().ToList();
            foreach (MultimediaTypeData mt in allMimeTypes)
            {
                foreach (string ext in mt.FileExtensions)
                {
                    if (Path.GetExtension(filePath).ToLower().Replace(".", string.Empty) == ext.ToLower().Replace(".", string.Empty))
                        return mt.Id;
                }
            }
            return String.Empty;
        }

        public static List<ItemInfo> FindCheckedOutItems(string host, string username, string password)
        {
            if (!EnsureValidClient(host, username, password))
                return null;

            return Client.GetSystemWideListXml(new RepositoryLocalObjectsFilterData()).ToList();
        }

        public static bool IsCheckedOut(string host, string username, string password, string id)
        {
            return FindCheckedOutItems(host, username, password).Any(x => x.TcmId == id);
        }

        public static FieldType GetFieldType(this ItemFieldDefinitionData field)
        {
            if (field is SingleLineTextFieldDefinitionData)
            {
                return FieldType.SingleLineText;
            }
            if (field is MultiLineTextFieldDefinitionData)
            {
                return FieldType.MultiLineText;
            }
            if (field is XhtmlFieldDefinitionData)
            {
                return FieldType.Xhtml;
            }
            if (field is DateFieldDefinitionData)
            {
                return FieldType.Date;
            }
            if (field is NumberFieldDefinitionData)
            {
                return FieldType.Number;
            }
            if (field is KeywordFieldDefinitionData)
            {
                return FieldType.Keyword;
            }
            if (field is MultimediaLinkFieldDefinitionData)
            {
                return FieldType.Multimedia;
            }
            if (field is ExternalLinkFieldDefinitionData)
            {
                return FieldType.ExternalLink;
            }
            if (field is ComponentLinkFieldDefinitionData)
            {
                return FieldType.ComponentLink;
            }
            if (field is EmbeddedSchemaFieldDefinitionData)
            {
                return FieldType.EmbeddedSchema;
            }
            
            return FieldType.None;
        }

        public static string GetFieldTypeName(this ItemFieldDefinitionData field)
        {
            return field.GetFieldType() == FieldType.None ? string.Empty : field.GetFieldType().ToString();
        }

        public static bool IsText(this ItemFieldDefinitionData field)
        {
            return field is SingleLineTextFieldDefinitionData && !field.IsTextSelect() || field is MultiLineTextFieldDefinitionData;
        }

        public static bool IsRichText(this ItemFieldDefinitionData field)
        {
            return field is XhtmlFieldDefinitionData;
        }

        public static bool IsDate(this ItemFieldDefinitionData field)
        {
            return field is DateFieldDefinitionData;
        }

        public static bool IsNumber(this ItemFieldDefinitionData field)
        {
            return field is NumberFieldDefinitionData;
        }

        public static bool IsKeyword(this ItemFieldDefinitionData field)
        {
            return field is KeywordFieldDefinitionData;
        }

        public static bool IsTextSelect(this ItemFieldDefinitionData field)
        {
            if (field is SingleLineTextFieldDefinitionData)
            {
                SingleLineTextFieldDefinitionData textField = (SingleLineTextFieldDefinitionData)field;
                return textField.List != null && textField.List.Entries != null && textField.List.Entries.Length > 0;
            }
            return false;
        }

        public static bool IsEmbedded(this ItemFieldDefinitionData field)
        {
            return field is EmbeddedSchemaFieldDefinitionData;
        }

        public static bool IsComponentLink(this ItemFieldDefinitionData field)
        {
            return field is ComponentLinkFieldDefinitionData;
        }

        public static bool IsMultiValue(this ItemFieldDefinitionData field)
        {
            return field.MaxOccurs == -1;
        }
        
        public static bool IsMandatory(this ItemFieldDefinitionData field)
        {
            return field.MinOccurs == 1;
        }

        #endregion

        #region Text helpers

        public static string GetPublicationTcmId(string id)
        {
            return "tcm:0-" + id.Replace("tcm:", string.Empty).Split('-')[0] + "-1";
        }

        public static string GetBluePrintItemTcmId(string id, string publicationId)
        {
            return "tcm:" + publicationId.Split('-')[1] + "-" + id.Split('-')[1] + (id.Split('-').Length > 2 ? "-" + id.Split('-')[2] : "");
        }

        public static string CutPath(this string path, string separator, int maxLength)
        {
            if (path == null || path.Length <= maxLength)
                return path;

            var list = path.Split(new[] { separator[0] });
            int itemMaxLength = maxLength / list.Length;

            return String.Join(separator, list.Select(item => item.Cut(itemMaxLength)).ToList());
        }

        public static string CutPath(this string path, string separator, int maxLength, bool fullLastItem)
        {
            if (path == null || path.Length <= maxLength)
                return path;

            if (!fullLastItem)
                return path.CutPath(separator, maxLength);

            string lastItem = path.Substring(path.LastIndexOf(separator, StringComparison.Ordinal));

            if (lastItem.Length > maxLength)
                return path.CutPath(separator, maxLength);

            return path.Substring(0, path.LastIndexOf(separator, StringComparison.Ordinal)).CutPath(separator, maxLength - lastItem.Length) + lastItem;
        }

        public static string Cut(this string str, int maxLength)
        {
            if (maxLength < 5)
                maxLength = 5;

            if (str.Length > maxLength)
            {
                return str.Substring(0, maxLength - 2) + "..";

            }
            return str;
        }

        public static string PrettyXml(this string xml)
        {
            try
            {
                return XElement.Parse(xml).ToString();
            }
            catch (Exception)
            {
                return xml;
            }
        }

        public static string PlainXml(this string xml)
        {
            try
            {
                return Regex.Replace(xml, "\\s+", " ").Replace("> <", "><");
            }
            catch (Exception)
            {
                return xml;
            }
        }

        public static string GetXml(this XElement node)
        {
            return node == null ? string.Empty : node.ToString();
        }

        public static string GetInnerXml(this XElement node)
        {
            var reader = node.CreateReader();
            reader.MoveToContent();
            return reader.ReadInnerXml();
        }

        public static string GetFieldFullName(this ItemFieldDefinitionData field)
        {
            if (field.IsEmbedded())
                return string.Format("{0} | {1}", field.Name, ((EmbeddedSchemaFieldDefinitionData)field).EmbeddedSchema.Title);

            if (field.IsComponentLink())
            {
                ComponentLinkFieldDefinitionData componentLinkField = ((ComponentLinkFieldDefinitionData)field);
                if (componentLinkField.AllowedTargetSchemas.Any())
                    return string.Format("{0} | {1}", componentLinkField.Name, componentLinkField.AllowedTargetSchemas[0].Title);
            }

            return string.Format("{0} | {1}", field.Name, field.GetFieldTypeName());
        }

        #endregion

        #region Isolated storage

        public static string GetFromIsolatedStorage(string key)
        {
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, typeof(System.Security.Policy.Url), typeof(System.Security.Policy.Url)))
            {
                if (!isf.FileExists(key + ".txt"))
                    return String.Empty;

                using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(key + ".txt", FileMode.Open, isf))
                {
                    using (StreamReader sr = new StreamReader(isfs, Encoding.UTF8))
                    {
                        return sr.ReadToEnd();
                    }
                }
            }
        }

        public static void SaveToIsolatedStorage(string key, string value)
        {
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, typeof(System.Security.Policy.Url), typeof(System.Security.Policy.Url)))
            {
                using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(key + ".txt", FileMode.Create, isf))
                {
                    byte[] data = Encoding.UTF8.GetBytes(value);
                    isfs.Write(data, 0, data.Length);
                }
            }
        }

        #endregion
    }
}